summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--.ci/templates/build-msvc.yml4
-rw-r--r--.ci/yuzu-patreon-step2.yml1
-rw-r--r--.gitmodules11
-rw-r--r--.lgtm.yml10
-rwxr-xr-x.travis/linux-mingw/docker.sh10
-rwxr-xr-x.travis/linux/docker.sh2
-rwxr-xr-x.travis/macos/build.sh3
-rw-r--r--CMakeLists.txt95
-rw-r--r--CMakeModules/CopyYuzuFFmpegDeps.cmake10
-rw-r--r--CMakeModules/CopyYuzuUnicornDeps.cmake9
-rw-r--r--dist/icons/controller/applet_dual_joycon.pngbin0 -> 3554 bytes
-rw-r--r--dist/icons/controller/applet_dual_joycon_dark.pngbin0 -> 3554 bytes
-rw-r--r--dist/icons/controller/applet_dual_joycon_dark_disabled.pngbin0 -> 3527 bytes
-rw-r--r--dist/icons/controller/applet_dual_joycon_disabled.pngbin0 -> 3314 bytes
-rw-r--r--dist/icons/controller/applet_dual_joycon_midnight.pngbin0 -> 3549 bytes
-rw-r--r--dist/icons/controller/applet_dual_joycon_midnight_disabled.pngbin0 -> 3584 bytes
-rw-r--r--dist/icons/controller/applet_handheld.pngbin0 -> 1671 bytes
-rw-r--r--dist/icons/controller/applet_handheld_dark.pngbin0 -> 1637 bytes
-rw-r--r--dist/icons/controller/applet_handheld_dark_disabled.pngbin0 -> 2642 bytes
-rw-r--r--dist/icons/controller/applet_handheld_disabled.pngbin0 -> 2221 bytes
-rw-r--r--dist/icons/controller/applet_handheld_midnight.pngbin0 -> 1644 bytes
-rw-r--r--dist/icons/controller/applet_handheld_midnight_disabled.pngbin0 -> 2634 bytes
-rw-r--r--dist/icons/controller/applet_pro_controller.pngbin0 -> 4382 bytes
-rw-r--r--dist/icons/controller/applet_pro_controller_dark.pngbin0 -> 4236 bytes
-rw-r--r--dist/icons/controller/applet_pro_controller_dark_disabled.pngbin0 -> 4477 bytes
-rw-r--r--dist/icons/controller/applet_pro_controller_disabled.pngbin0 -> 4173 bytes
-rw-r--r--dist/icons/controller/applet_pro_controller_midnight.pngbin0 -> 4376 bytes
-rw-r--r--dist/icons/controller/applet_pro_controller_midnight_disabled.pngbin0 -> 4459 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_left.pngbin0 -> 2083 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_left_dark.pngbin0 -> 2067 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_left_dark_disabled.pngbin0 -> 2520 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_left_disabled.pngbin0 -> 2179 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_left_midnight.pngbin0 -> 2065 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_left_midnight_disabled.pngbin0 -> 2529 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_right.pngbin0 -> 2150 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_right_dark.pngbin0 -> 2146 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_right_dark_disabled.pngbin0 -> 2556 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_right_disabled.pngbin0 -> 2212 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_right_midnight.pngbin0 -> 2150 bytes
-rw-r--r--dist/icons/controller/applet_single_joycon_right_midnight_disabled.pngbin0 -> 2611 bytes
-rw-r--r--dist/icons/controller/controller.qrc55
-rw-r--r--dist/icons/controller/dual_joycon.pngbin0 -> 36466 bytes
-rw-r--r--dist/icons/controller/dual_joycon_dark.pngbin0 -> 36261 bytes
-rw-r--r--dist/icons/controller/dual_joycon_midnight.pngbin0 -> 34667 bytes
-rw-r--r--dist/icons/controller/handheld.pngbin0 -> 14108 bytes
-rw-r--r--dist/icons/controller/handheld_dark.pngbin0 -> 13731 bytes
-rw-r--r--dist/icons/controller/handheld_midnight.pngbin0 -> 13366 bytes
-rw-r--r--dist/icons/controller/pro_controller.pngbin0 -> 36710 bytes
-rw-r--r--dist/icons/controller/pro_controller_dark.pngbin0 -> 34897 bytes
-rw-r--r--dist/icons/controller/pro_controller_midnight.pngbin0 -> 35893 bytes
-rw-r--r--dist/icons/controller/single_joycon_left.pngbin0 -> 25565 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_dark.pngbin0 -> 25682 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_midnight.pngbin0 -> 24405 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical.pngbin0 -> 24764 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical_dark.pngbin0 -> 24938 bytes
-rw-r--r--dist/icons/controller/single_joycon_left_vertical_midnight.pngbin0 -> 23681 bytes
-rw-r--r--dist/icons/controller/single_joycon_right.pngbin0 -> 28320 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_dark.pngbin0 -> 28157 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_midnight.pngbin0 -> 27006 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical.pngbin0 -> 27655 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical_dark.pngbin0 -> 27729 bytes
-rw-r--r--dist/icons/controller/single_joycon_right_vertical_midnight.pngbin0 -> 26354 bytes
-rw-r--r--dist/languages/de.ts4749
-rw-r--r--dist/languages/es.ts4757
-rw-r--r--dist/languages/fr.ts4732
-rw-r--r--dist/languages/it.ts4724
-rw-r--r--dist/languages/ja_JP.ts4752
-rw-r--r--dist/languages/nl.ts4719
-rw-r--r--dist/languages/pl.ts4713
-rw-r--r--dist/languages/pt_BR.ts4757
-rw-r--r--dist/languages/pt_PT.ts4725
-rw-r--r--dist/languages/ru_RU.ts4720
-rw-r--r--dist/languages/zh_CN.ts4747
-rw-r--r--dist/license.md3
-rw-r--r--dist/qt_themes/colorful_dark/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_dark/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_dark/style.qrc1
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/colorful_midnight_blue/style.qrc1
-rw-r--r--dist/qt_themes/default/default.qrc1
-rw-r--r--dist/qt_themes/default/icons/16x16/refresh.pngbin0 -> 349 bytes
-rw-r--r--dist/qt_themes/default/icons/16x16/view-refresh.pngbin0 -> 349 bytes
-rw-r--r--dist/qt_themes/default/style.qss250
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle/style.qrc3
-rw-r--r--dist/qt_themes/qdarkstyle/style.qss322
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.pngbin0 -> 362 bytes
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qrc3
-rw-r--r--dist/qt_themes/qdarkstyle_midnight_blue/style.qss481
-rw-r--r--externals/CMakeLists.txt28
-rw-r--r--externals/find-modules/FindFFmpeg.cmake100
-rw-r--r--externals/httplib/README.md2
-rw-r--r--externals/httplib/httplib.h4762
m---------externals/inih/inih0
m---------externals/libressl0
m---------externals/libusb0
-rw-r--r--externals/libusb/CMakeLists.txt150
-rw-r--r--externals/libusb/config.h.in90
m---------externals/libusb/libusb0
-rw-r--r--externals/lurlparser/CMakeLists.txt8
-rw-r--r--externals/lurlparser/LUrlParser.cpp265
-rw-r--r--externals/lurlparser/LUrlParser.h78
-rw-r--r--externals/lurlparser/README.md19
-rw-r--r--externals/microprofile/microprofile.h77
-rw-r--r--externals/microprofile/microprofileui.h212
m---------externals/unicorn0
m---------externals/xbyak0
-rw-r--r--src/CMakeLists.txt9
-rw-r--r--src/audio_core/CMakeLists.txt32
-rw-r--r--src/audio_core/algorithm/filter.cpp3
-rw-r--r--src/audio_core/algorithm/interpolate.cpp37
-rw-r--r--src/audio_core/algorithm/interpolate.h3
-rw-r--r--src/audio_core/audio_renderer.cpp548
-rw-r--r--src/audio_core/audio_renderer.h225
-rw-r--r--src/audio_core/behavior_info.cpp85
-rw-r--r--src/audio_core/behavior_info.h58
-rw-r--r--src/audio_core/codec.cpp5
-rw-r--r--src/audio_core/codec.h2
-rw-r--r--src/audio_core/command_generator.cpp977
-rw-r--r--src/audio_core/command_generator.h102
-rw-r--r--src/audio_core/common.h66
-rw-r--r--src/audio_core/cubeb_sink.cpp28
-rw-r--r--src/audio_core/effect_context.cpp299
-rw-r--r--src/audio_core/effect_context.h321
-rw-r--r--src/audio_core/info_updater.cpp516
-rw-r--r--src/audio_core/info_updater.h58
-rw-r--r--src/audio_core/memory_pool.cpp62
-rw-r--r--src/audio_core/memory_pool.h53
-rw-r--r--src/audio_core/mix_context.cpp296
-rw-r--r--src/audio_core/mix_context.h114
-rw-r--r--src/audio_core/sink_context.cpp31
-rw-r--r--src/audio_core/sink_context.h89
-rw-r--r--src/audio_core/splitter_context.cpp617
-rw-r--r--src/audio_core/splitter_context.h221
-rw-r--r--src/audio_core/stream.cpp7
-rw-r--r--src/audio_core/voice_context.cpp529
-rw-r--r--src/audio_core/voice_context.h296
-rw-r--r--src/common/CMakeLists.txt26
-rw-r--r--src/common/assert.h7
-rw-r--r--src/common/bit_cast.h22
-rw-r--r--src/common/color.h4
-rw-r--r--src/common/common_funcs.h14
-rw-r--r--src/common/fiber.cpp159
-rw-r--r--src/common/fiber.h20
-rw-r--r--src/common/file_util.cpp31
-rw-r--r--src/common/file_util.h2
-rw-r--r--src/common/hex_util.h10
-rw-r--r--src/common/logging/backend.cpp3
-rw-r--r--src/common/logging/log.h1
-rw-r--r--src/common/math_util.h10
-rw-r--r--src/common/misc.cpp15
-rw-r--r--src/common/page_table.cpp2
-rw-r--r--src/common/page_table.h14
-rw-r--r--src/common/param_package.h2
-rw-r--r--src/common/quaternion.h30
-rw-r--r--src/common/spin_lock.h8
-rw-r--r--src/common/stream.cpp47
-rw-r--r--src/common/stream.h56
-rw-r--r--src/common/string_util.cpp5
-rw-r--r--src/common/thread.cpp12
-rw-r--r--src/common/thread.h9
-rw-r--r--src/common/timer.cpp12
-rw-r--r--src/common/vector_math.h75
-rw-r--r--src/common/virtual_buffer.cpp4
-rw-r--r--src/common/virtual_buffer.h36
-rw-r--r--src/common/wall_clock.cpp4
-rw-r--r--src/common/wall_clock.h2
-rw-r--r--src/common/x64/native_clock.h4
-rw-r--r--src/common/x64/xbyak_abi.h32
-rw-r--r--src/core/CMakeLists.txt45
-rw-r--r--src/core/arm/arm_interface.cpp12
-rw-r--r--src/core/arm/cpu_interrupt_handler.cpp6
-rw-r--r--src/core/arm/cpu_interrupt_handler.h7
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_32.cpp1
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.cpp29
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_64.h2
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.cpp6
-rw-r--r--src/core/arm/dynarmic/arm_dynarmic_cp15.h4
-rw-r--r--src/core/arm/unicorn/arm_unicorn.cpp295
-rw-r--r--src/core/arm/unicorn/arm_unicorn.h63
-rw-r--r--src/core/core.cpp40
-rw-r--r--src/core/core.h14
-rw-r--r--src/core/cpu_manager.cpp4
-rw-r--r--src/core/crypto/key_manager.cpp9
-rw-r--r--src/core/crypto/key_manager.h6
-rw-r--r--src/core/crypto/partition_data_manager.cpp1
-rw-r--r--src/core/file_sys/bis_factory.cpp11
-rw-r--r--src/core/file_sys/bis_factory.h5
-rw-r--r--src/core/file_sys/card_image.cpp5
-rw-r--r--src/core/file_sys/card_image.h7
-rw-r--r--src/core/file_sys/content_archive.cpp15
-rw-r--r--src/core/file_sys/content_archive.h2
-rw-r--r--src/core/file_sys/control_metadata.cpp1
-rw-r--r--src/core/file_sys/control_metadata.h4
-rw-r--r--src/core/file_sys/fsmitm_romfsbuild.cpp10
-rw-r--r--src/core/file_sys/ips_layer.cpp8
-rw-r--r--src/core/file_sys/kernel_executable.cpp3
-rw-r--r--src/core/file_sys/kernel_executable.h9
-rw-r--r--src/core/file_sys/nca_metadata.cpp3
-rw-r--r--src/core/file_sys/nca_metadata.h2
-rw-r--r--src/core/file_sys/nca_patch.cpp83
-rw-r--r--src/core/file_sys/nca_patch.h4
-rw-r--r--src/core/file_sys/partition_filesystem.cpp8
-rw-r--r--src/core/file_sys/partition_filesystem.h8
-rw-r--r--src/core/file_sys/patch_manager.cpp222
-rw-r--r--src/core/file_sys/patch_manager.h63
-rw-r--r--src/core/file_sys/program_metadata.cpp1
-rw-r--r--src/core/file_sys/program_metadata.h2
-rw-r--r--src/core/file_sys/registered_cache.cpp33
-rw-r--r--src/core/file_sys/romfs.h1
-rw-r--r--src/core/file_sys/romfs_factory.cpp56
-rw-r--r--src/core/file_sys/romfs_factory.h21
-rw-r--r--src/core/file_sys/sdmc_factory.cpp2
-rw-r--r--src/core/file_sys/sdmc_factory.h2
-rw-r--r--src/core/file_sys/submission_package.cpp96
-rw-r--r--src/core/file_sys/submission_package.h7
-rw-r--r--src/core/file_sys/vfs.cpp7
-rw-r--r--src/core/file_sys/vfs_offset.cpp7
-rw-r--r--src/core/file_sys/vfs_static.h8
-rw-r--r--src/core/file_sys/xts_archive.cpp10
-rw-r--r--src/core/file_sys/xts_archive.h10
-rw-r--r--src/core/frontend/applets/controller.cpp81
-rw-r--r--src/core/frontend/applets/controller.h56
-rw-r--r--src/core/frontend/emu_window.cpp10
-rw-r--r--src/core/frontend/framebuffer_layout.cpp8
-rw-r--r--src/core/frontend/framebuffer_layout.h1
-rw-r--r--src/core/frontend/input.h32
-rw-r--r--src/core/gdbstub/gdbstub.cpp30
-rw-r--r--src/core/hle/ipc_helpers.h28
-rw-r--r--src/core/hle/kernel/client_session.cpp5
-rw-r--r--src/core/hle/kernel/client_session.h7
-rw-r--r--src/core/hle/kernel/handle_table.cpp2
-rw-r--r--src/core/hle/kernel/hle_ipc.h6
-rw-r--r--src/core/hle/kernel/kernel.cpp74
-rw-r--r--src/core/hle/kernel/physical_core.cpp16
-rw-r--r--src/core/hle/kernel/process.cpp3
-rw-r--r--src/core/hle/kernel/scheduler.cpp18
-rw-r--r--src/core/hle/kernel/scheduler.h2
-rw-r--r--src/core/hle/kernel/server_session.cpp6
-rw-r--r--src/core/hle/kernel/server_session.h11
-rw-r--r--src/core/hle/kernel/svc.cpp2
-rw-r--r--src/core/hle/kernel/thread.cpp17
-rw-r--r--src/core/hle/service/acc/acc.cpp26
-rw-r--r--src/core/hle/service/acc/acc.h1
-rw-r--r--src/core/hle/service/acc/acc_u0.cpp2
-rw-r--r--src/core/hle/service/am/am.cpp30
-rw-r--r--src/core/hle/service/am/am.h2
-rw-r--r--src/core/hle/service/am/applet_ae.cpp2
-rw-r--r--src/core/hle/service/am/applets/applets.cpp72
-rw-r--r--src/core/hle/service/am/applets/applets.h19
-rw-r--r--src/core/hle/service/am/applets/controller.cpp252
-rw-r--r--src/core/hle/service/am/applets/controller.h135
-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/audio/audren_u.cpp149
-rw-r--r--src/core/hle/service/bcat/backend/backend.cpp2
-rw-r--r--src/core/hle/service/bcat/backend/boxcat.cpp20
-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_c.cpp16
-rw-r--r--src/core/hle/service/caps/caps_c.h3
-rw-r--r--src/core/hle/service/caps/caps_su.cpp7
-rw-r--r--src/core/hle/service/caps/caps_u.cpp40
-rw-r--r--src/core/hle/service/caps/caps_u.h2
-rw-r--r--src/core/hle/service/filesystem/filesystem.cpp14
-rw-r--r--src/core/hle/service/filesystem/fsp_srv.cpp24
-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/controller_base.h4
-rw-r--r--src/core/hle/service/hid/controllers/keyboard.cpp4
-rw-r--r--src/core/hle/service/hid/controllers/npad.cpp831
-rw-r--r--src/core/hle/service/hid/controllers/npad.h215
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.cpp12
-rw-r--r--src/core/hle/service/hid/controllers/touchscreen.h1
-rw-r--r--src/core/hle/service/hid/hid.cpp745
-rw-r--r--src/core/hle/service/hid/hid.h40
-rw-r--r--src/core/hle/service/ldr/ldr.cpp21
-rw-r--r--src/core/hle/service/lm/lm.cpp1
-rw-r--r--src/core/hle/service/mii/manager.cpp6
-rw-r--r--src/core/hle/service/mii/mii.cpp1
-rw-r--r--src/core/hle/service/nfp/nfp.cpp23
-rw-r--r--src/core/hle/service/nifm/nifm.cpp25
-rw-r--r--src/core/hle/service/ns/ns.cpp25
-rw-r--r--src/core/hle/service/ns/ns.h28
-rw-r--r--src/core/hle/service/ns/pl_u.cpp12
-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.cpp169
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_as_gpu.h109
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp120
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_ctrl.h113
-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.cpp294
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_gpu.h169
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp72
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec.h32
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp229
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h196
-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.cpp64
-rw-r--r--src/core/hle/service/nvdrv/devices/nvhost_vic.h37
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.cpp119
-rw-r--r--src/core/hle/service/nvdrv/devices/nvmap.h69
-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.cpp119
-rw-r--r--src/core/hle/service/nvdrv/nvdrv.h39
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.cpp39
-rw-r--r--src/core/hle/service/nvdrv/syncpoint_manager.h85
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.cpp18
-rw-r--r--src/core/hle/service/nvflinger/buffer_queue.h1
-rw-r--r--src/core/hle/service/nvflinger/nvflinger.cpp10
-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.cpp15
-rw-r--r--src/core/hle/service/service.h4
-rw-r--r--src/core/hle/service/set/set.cpp1
-rw-r--r--src/core/hle/service/set/set_sys.cpp2
-rw-r--r--src/core/hle/service/sm/sm.cpp13
-rw-r--r--src/core/hle/service/sm/sm.h7
-rw-r--r--src/core/hle/service/sockets/blocking_worker.h161
-rw-r--r--src/core/hle/service/sockets/bsd.cpp810
-rw-r--r--src/core/hle/service/sockets/bsd.h150
-rw-r--r--src/core/hle/service/sockets/sockets.cpp6
-rw-r--r--src/core/hle/service/sockets/sockets.h85
-rw-r--r--src/core/hle/service/sockets/sockets_translate.cpp165
-rw-r--r--src/core/hle/service/sockets/sockets_translate.h48
-rw-r--r--src/core/hle/service/time/time.cpp28
-rw-r--r--src/core/hle/service/time/time.h9
-rw-r--r--src/core/hle/service/time/time_manager.cpp359
-rw-r--r--src/core/hle/service/time/time_manager.h85
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.cpp5
-rw-r--r--src/core/hle/service/time/time_zone_content_manager.h4
-rw-r--r--src/core/hle/service/time/time_zone_manager.cpp21
-rw-r--r--src/core/hle/service/vi/vi.cpp56
-rw-r--r--src/core/loader/deconstructed_rom_directory.cpp24
-rw-r--r--src/core/loader/deconstructed_rom_directory.h6
-rw-r--r--src/core/loader/elf.cpp3
-rw-r--r--src/core/loader/elf.h6
-rw-r--r--src/core/loader/kip.cpp5
-rw-r--r--src/core/loader/kip.h6
-rw-r--r--src/core/loader/loader.cpp27
-rw-r--r--src/core/loader/loader.h16
-rw-r--r--src/core/loader/nax.cpp5
-rw-r--r--src/core/loader/nax.h8
-rw-r--r--src/core/loader/nca.cpp8
-rw-r--r--src/core/loader/nca.h6
-rw-r--r--src/core/loader/nro.cpp8
-rw-r--r--src/core/loader/nro.h6
-rw-r--r--src/core/loader/nso.cpp17
-rw-r--r--src/core/loader/nso.h14
-rw-r--r--src/core/loader/nsp.cpp26
-rw-r--r--src/core/loader/nsp.h11
-rw-r--r--src/core/loader/xci.cpp23
-rw-r--r--src/core/loader/xci.h11
-rw-r--r--src/core/memory.cpp14
-rw-r--r--src/core/memory/cheat_engine.cpp44
-rw-r--r--src/core/memory/cheat_engine.h5
-rw-r--r--src/core/network/network.cpp50
-rw-r--r--src/core/settings.cpp81
-rw-r--r--src/core/settings.h408
-rw-r--r--src/core/telemetry_session.cpp13
-rw-r--r--src/core/telemetry_session.h18
-rw-r--r--src/input_common/CMakeLists.txt37
-rwxr-xr-xsrc/input_common/analog_from_button.cpp18
-rw-r--r--src/input_common/gcadapter/gc_adapter.cpp520
-rw-r--r--src/input_common/gcadapter/gc_adapter.h149
-rw-r--r--src/input_common/gcadapter/gc_poller.cpp206
-rw-r--r--src/input_common/gcadapter/gc_poller.h11
-rw-r--r--src/input_common/keyboard.cpp5
-rw-r--r--src/input_common/main.cpp264
-rw-r--r--src/input_common/main.h152
-rw-r--r--src/input_common/motion_emu.cpp58
-rw-r--r--src/input_common/motion_from_button.cpp34
-rw-r--r--src/input_common/motion_from_button.h25
-rw-r--r--src/input_common/motion_input.cpp301
-rw-r--r--src/input_common/motion_input.h74
-rw-r--r--src/input_common/sdl/sdl.h19
-rw-r--r--src/input_common/sdl/sdl_impl.cpp751
-rw-r--r--src/input_common/sdl/sdl_impl.h12
-rw-r--r--src/input_common/settings.cpp47
-rw-r--r--src/input_common/settings.h371
-rw-r--r--src/input_common/touch_from_button.cpp52
-rw-r--r--src/input_common/touch_from_button.h23
-rw-r--r--src/input_common/udp/client.cpp226
-rw-r--r--src/input_common/udp/client.h89
-rw-r--r--src/input_common/udp/udp.cpp175
-rw-r--r--src/input_common/udp/udp.h54
-rw-r--r--src/tests/common/fibers.cpp71
-rw-r--r--src/video_core/CMakeLists.txt53
-rw-r--r--src/video_core/buffer_cache/buffer_cache.h51
-rw-r--r--src/video_core/cdma_pusher.cpp171
-rw-r--r--src/video_core/cdma_pusher.h138
-rw-r--r--src/video_core/command_classes/codecs/codec.cpp115
-rw-r--r--src/video_core/command_classes/codecs/codec.h66
-rw-r--r--src/video_core/command_classes/codecs/h264.cpp293
-rw-r--r--src/video_core/command_classes/codecs/h264.h118
-rw-r--r--src/video_core/command_classes/codecs/vp9.cpp1040
-rw-r--r--src/video_core/command_classes/codecs/vp9.h196
-rw-r--r--src/video_core/command_classes/codecs/vp9_types.h366
-rw-r--r--src/video_core/command_classes/host1x.cpp39
-rw-r--r--src/video_core/command_classes/host1x.h78
-rw-r--r--src/video_core/command_classes/nvdec.cpp52
-rw-r--r--src/video_core/command_classes/nvdec.h39
-rw-r--r--src/video_core/command_classes/nvdec_common.h48
-rw-r--r--src/video_core/command_classes/sync_manager.cpp60
-rw-r--r--src/video_core/command_classes/sync_manager.h64
-rw-r--r--src/video_core/command_classes/vic.cpp180
-rw-r--r--src/video_core/command_classes/vic.h110
-rw-r--r--src/video_core/dma_pusher.cpp56
-rw-r--r--src/video_core/dma_pusher.h47
-rw-r--r--src/video_core/engines/fermi_2d.cpp12
-rw-r--r--src/video_core/engines/fermi_2d.h4
-rw-r--r--src/video_core/engines/maxwell_3d.cpp267
-rw-r--r--src/video_core/engines/maxwell_3d.h8
-rw-r--r--src/video_core/engines/maxwell_dma.cpp2
-rw-r--r--src/video_core/engines/shader_bytecode.h186
-rw-r--r--src/video_core/engines/shader_header.h13
-rw-r--r--src/video_core/fence_manager.h27
-rw-r--r--src/video_core/gpu.cpp63
-rw-r--r--src/video_core/gpu.h107
-rw-r--r--src/video_core/gpu_asynch.cpp26
-rw-r--r--src/video_core/gpu_asynch.h3
-rw-r--r--src/video_core/gpu_synch.cpp18
-rw-r--r--src/video_core/gpu_synch.h3
-rw-r--r--src/video_core/gpu_thread.cpp16
-rw-r--r--src/video_core/gpu_thread.h19
-rw-r--r--src/video_core/host_shaders/CMakeLists.txt36
-rw-r--r--src/video_core/host_shaders/StringShaderHeader.cmake13
-rw-r--r--src/video_core/host_shaders/opengl_present.frag10
-rw-r--r--src/video_core/host_shaders/opengl_present.vert24
-rw-r--r--src/video_core/host_shaders/source_shader.h.in9
-rw-r--r--src/video_core/macro/macro.cpp2
-rw-r--r--src/video_core/macro/macro_jit_x64.cpp10
-rw-r--r--src/video_core/memory_manager.cpp22
-rw-r--r--src/video_core/memory_manager.h37
-rw-r--r--src/video_core/query_cache.h37
-rw-r--r--src/video_core/rasterizer_interface.h24
-rw-r--r--src/video_core/renderer_base.h27
-rw-r--r--src/video_core/renderer_opengl/gl_arb_decompiler.cpp72
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_buffer_cache.h3
-rw-r--r--src/video_core/renderer_opengl/gl_device.cpp1
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.cpp5
-rw-r--r--src/video_core/renderer_opengl/gl_fence_manager.h6
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_query_cache.h14
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.cpp314
-rw-r--r--src/video_core/renderer_opengl/gl_rasterizer.h35
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.cpp9
-rw-r--r--src/video_core/renderer_opengl/gl_resource_manager.h3
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.cpp64
-rw-r--r--src/video_core/renderer_opengl/gl_shader_cache.h23
-rw-r--r--src/video_core/renderer_opengl/gl_shader_decompiler.cpp36
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.cpp13
-rw-r--r--src/video_core/renderer_opengl/gl_shader_disk_cache.h16
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.cpp15
-rw-r--r--src/video_core/renderer_opengl/gl_shader_util.h2
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.cpp6
-rw-r--r--src/video_core/renderer_opengl/gl_state_tracker.h28
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.cpp10
-rw-r--r--src/video_core/renderer_opengl/gl_texture_cache.h6
-rw-r--r--src/video_core/renderer_opengl/maxwell_to_gl.h6
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.cpp349
-rw-r--r--src/video_core/renderer_opengl/renderer_opengl.h37
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.cpp3
-rw-r--r--src/video_core/renderer_vulkan/fixed_pipeline_state.h10
-rw-r--r--src/video_core/renderer_vulkan/maxwell_to_vk.cpp24
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.cpp75
-rw-r--r--src/video_core/renderer_vulkan/renderer_vulkan.h24
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.cpp37
-rw-r--r--src/video_core/renderer_vulkan/vk_blit_screen.h25
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.cpp17
-rw-r--r--src/video_core/renderer_vulkan/vk_buffer_cache.h7
-rw-r--r--src/video_core/renderer_vulkan/vk_command_pool.cpp46
-rw-r--r--src/video_core/renderer_vulkan/vk_command_pool.h34
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.cpp23
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pass.h4
-rw-r--r--src/video_core/renderer_vulkan/vk_compute_pipeline.cpp2
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_descriptor_pool.h15
-rw-r--r--src/video_core/renderer_vulkan/vk_device.cpp114
-rw-r--r--src/video_core/renderer_vulkan/vk_device.h12
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.cpp18
-rw-r--r--src/video_core/renderer_vulkan/vk_fence_manager.h8
-rw-r--r--src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp9
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.cpp56
-rw-r--r--src/video_core/renderer_vulkan/vk_master_semaphore.h70
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.cpp95
-rw-r--r--src/video_core/renderer_vulkan/vk_pipeline_cache.h29
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.cpp57
-rw-r--r--src/video_core/renderer_vulkan/vk_query_cache.h23
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.cpp142
-rw-r--r--src/video_core/renderer_vulkan/vk_rasterizer.h20
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.cpp311
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_manager.h196
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_pool.cpp63
-rw-r--r--src/video_core/renderer_vulkan/vk_resource_pool.h43
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.cpp77
-rw-r--r--src/video_core/renderer_vulkan/vk_scheduler.h70
-rw-r--r--src/video_core/renderer_vulkan/vk_shader_decompiler.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp49
-rw-r--r--src/video_core/renderer_vulkan/vk_staging_buffer_pool.h14
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.cpp19
-rw-r--r--src/video_core/renderer_vulkan/vk_state_tracker.h28
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.cpp12
-rw-r--r--src/video_core/renderer_vulkan/vk_stream_buffer.h5
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.cpp20
-rw-r--r--src/video_core/renderer_vulkan/vk_swapchain.h9
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.cpp25
-rw-r--r--src/video_core/renderer_vulkan/vk_texture_cache.h19
-rw-r--r--src/video_core/renderer_vulkan/wrapper.cpp96
-rw-r--r--src/video_core/renderer_vulkan/wrapper.h52
-rw-r--r--src/video_core/shader/ast.h25
-rw-r--r--src/video_core/shader/async_shaders.cpp55
-rw-r--r--src/video_core/shader/async_shaders.h37
-rw-r--r--src/video_core/shader/control_flow.cpp27
-rw-r--r--src/video_core/shader/decode/arithmetic.cpp3
-rw-r--r--src/video_core/shader/decode/arithmetic_half.cpp3
-rw-r--r--src/video_core/shader/decode/arithmetic_integer_immediate.cpp35
-rw-r--r--src/video_core/shader/decode/image.cpp15
-rw-r--r--src/video_core/shader/decode/memory.cpp3
-rw-r--r--src/video_core/shader/decode/texture.cpp44
-rw-r--r--src/video_core/shader/memory_util.cpp7
-rw-r--r--src/video_core/shader/memory_util.h6
-rw-r--r--src/video_core/shader/registry.cpp50
-rw-r--r--src/video_core/shader/registry.h2
-rw-r--r--src/video_core/shader/track.cpp4
-rw-r--r--src/video_core/texture_cache/surface_base.cpp10
-rw-r--r--src/video_core/texture_cache/surface_params.cpp12
-rw-r--r--src/video_core/texture_cache/surface_params.h5
-rw-r--r--src/video_core/texture_cache/texture_cache.h51
-rw-r--r--src/video_core/video_core.cpp16
-rw-r--r--src/web_service/CMakeLists.txt2
-rw-r--r--src/web_service/web_backend.cpp33
-rw-r--r--src/yuzu/CMakeLists.txt31
-rw-r--r--src/yuzu/aboutdialog.ui20
-rw-r--r--src/yuzu/applets/controller.cpp637
-rw-r--r--src/yuzu/applets/controller.h144
-rw-r--r--src/yuzu/applets/controller.ui2653
-rw-r--r--src/yuzu/applets/profile_select.cpp9
-rw-r--r--src/yuzu/applets/profile_select.h1
-rw-r--r--src/yuzu/bootmanager.cpp104
-rw-r--r--src/yuzu/bootmanager.h9
-rw-r--r--src/yuzu/configuration/config.cpp478
-rw-r--r--src/yuzu/configuration/config.h29
-rw-r--r--src/yuzu/configuration/configure.ui79
-rw-r--r--src/yuzu/configuration/configure_audio.cpp10
-rw-r--r--src/yuzu/configuration/configure_debug_controller.cpp43
-rw-r--r--src/yuzu/configuration/configure_debug_controller.h41
-rw-r--r--src/yuzu/configuration/configure_debug_controller.ui77
-rw-r--r--src/yuzu/configuration/configure_dialog.cpp15
-rw-r--r--src/yuzu/configuration/configure_dialog.h7
-rw-r--r--src/yuzu/configuration/configure_general.cpp8
-rw-r--r--src/yuzu/configuration/configure_graphics.cpp28
-rw-r--r--src/yuzu/configuration/configure_graphics.h1
-rw-r--r--src/yuzu/configuration/configure_graphics.ui7
-rw-r--r--src/yuzu/configuration/configure_graphics_advanced.cpp8
-rw-r--r--src/yuzu/configuration/configure_input.cpp312
-rw-r--r--src/yuzu/configuration/configure_input.h39
-rw-r--r--src/yuzu/configuration/configure_input.ui1033
-rw-r--r--src/yuzu/configuration/configure_input_advanced.cpp171
-rw-r--r--src/yuzu/configuration/configure_input_advanced.h46
-rw-r--r--src/yuzu/configuration/configure_input_advanced.ui2688
-rw-r--r--src/yuzu/configuration/configure_input_player.cpp1140
-rw-r--r--src/yuzu/configuration/configure_input_player.h150
-rw-r--r--src/yuzu/configuration/configure_input_player.ui4137
-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.ui71
-rw-r--r--src/yuzu/configuration/configure_input_simple.cpp152
-rw-r--r--src/yuzu/configuration/configure_input_simple.h43
-rw-r--r--src/yuzu/configuration/configure_input_simple.ui97
-rw-r--r--src/yuzu/configuration/configure_motion_touch.cpp314
-rw-r--r--src/yuzu/configuration/configure_motion_touch.h90
-rw-r--r--src/yuzu/configuration/configure_motion_touch.ui317
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.cpp56
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.h15
-rw-r--r--src/yuzu/configuration/configure_mouse_advanced.ui272
-rw-r--r--src/yuzu/configuration/configure_per_game.cpp12
-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_system.cpp36
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.cpp623
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.h92
-rw-r--r--src/yuzu/configuration/configure_touch_from_button.ui221
-rw-r--r--src/yuzu/configuration/configure_touch_widget.h62
-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.cpp85
-rw-r--r--src/yuzu/game_list.h24
-rw-r--r--src/yuzu/game_list_p.h34
-rw-r--r--src/yuzu/game_list_worker.cpp36
-rw-r--r--src/yuzu/install_dialog.h5
-rw-r--r--src/yuzu/main.cpp185
-rw-r--r--src/yuzu/main.h24
-rw-r--r--src/yuzu/main.ui2
-rw-r--r--src/yuzu/uisettings.cpp1
-rw-r--r--src/yuzu/uisettings.h4
-rw-r--r--src/yuzu_cmd/CMakeLists.txt2
-rw-r--r--src/yuzu_cmd/config.cpp29
-rw-r--r--src/yuzu_cmd/default_ini.h12
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.cpp17
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2.h15
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp14
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp8
-rw-r--r--src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h10
-rw-r--r--src/yuzu_cmd/yuzu.cpp17
-rw-r--r--src/yuzu_tester/CMakeLists.txt2
-rw-r--r--src/yuzu_tester/config.cpp13
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp5
-rw-r--r--src/yuzu_tester/emu_window/emu_window_sdl2_hide.h6
-rw-r--r--src/yuzu_tester/yuzu.cpp1
630 files changed, 93141 insertions, 12703 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:
version: ''
steps:
+- script: choco install vulkan-sdk
+ displayName: 'Install vulkan-sdk'
- script: python -m pip install --upgrade pip conan
displayName: 'Install conan'
-- 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 ..
+- 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 ..
displayName: 'Configure CMake'
- task: MSBuild@1
displayName: 'Build'
diff --git a/.ci/yuzu-patreon-step2.yml b/.ci/yuzu-patreon-step2.yml
index 41eccd973..3f338e2a0 100644
--- a/.ci/yuzu-patreon-step2.yml
+++ b/.ci/yuzu-patreon-step2.yml
@@ -9,6 +9,7 @@ stages:
displayName: 'build'
jobs:
- job: build
+ timeoutInMinutes: 120
displayName: 'windows-msvc'
pool:
vmImage: windows-2019
diff --git a/.gitmodules b/.gitmodules
index 79028bbb5..41022615b 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,21 +1,21 @@
[submodule "inih"]
path = externals/inih/inih
- url = https://github.com/svn2github/inih
+ url = https://github.com/benhoyt/inih.git
[submodule "cubeb"]
path = externals/cubeb
url = https://github.com/kinetiknz/cubeb.git
[submodule "dynarmic"]
path = externals/dynarmic
url = https://github.com/MerryMage/dynarmic.git
-[submodule "unicorn"]
- path = externals/unicorn
- url = https://github.com/yuzu-emu/unicorn
[submodule "soundtouch"]
path = externals/soundtouch
url = https://github.com/citra-emu/ext-soundtouch.git
[submodule "libressl"]
path = externals/libressl
url = https://github.com/citra-emu/ext-libressl-portable.git
+[submodule "libusb"]
+ path = externals/libusb/libusb
+ url = https://github.com/libusb/libusb.git
[submodule "discord-rpc"]
path = externals/discord-rpc
url = https://github.com/discordapp/discord-rpc.git
@@ -34,9 +34,6 @@
[submodule "xbyak"]
path = externals/xbyak
url = https://github.com/herumi/xbyak.git
-[submodule "externals/libusb"]
- path = externals/libusb
- url = https://github.com/ameerj/libusb
[submodule "opus"]
path = externals/opus/opus
url = https://github.com/xiph/opus.git
diff --git a/.lgtm.yml b/.lgtm.yml
new file mode 100644
index 000000000..5751d0e4c
--- /dev/null
+++ b/.lgtm.yml
@@ -0,0 +1,10 @@
+path_classifiers:
+ library: "externals"
+extraction:
+ cpp:
+ prepare:
+ packages:
+ - "libsdl2-dev"
+ - "qtmultimedia5-dev"
+ - "libtbb-dev"
+ - "libjack-jackd2-dev"
diff --git a/.travis/linux-mingw/docker.sh b/.travis/linux-mingw/docker.sh
index 28033acfb..80d7dfe9b 100755
--- a/.travis/linux-mingw/docker.sh
+++ b/.travis/linux-mingw/docker.sh
@@ -4,16 +4,8 @@ cd /yuzu
# override Travis CI unreasonable ccache size
echo 'max_size = 3.0G' > "$HOME/.ccache/ccache.conf"
-# Dirty hack to trick unicorn makefile into believing we are in a MINGW system
-mv /bin/uname /bin/uname1 && echo -e '#!/bin/sh\necho MINGW64' >> /bin/uname
-chmod +x /bin/uname
-
-# Dirty hack to trick unicorn makefile into believing we have cmd
-echo '' >> /bin/cmd
-chmod +x /bin/cmd
-
mkdir build && cd build
-cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DYUZU_USE_BUNDLED_UNICORN=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
+cmake .. -G Ninja -DCMAKE_TOOLCHAIN_FILE="$(pwd)/../CMakeModules/MinGWCross.cmake" -DUSE_CCACHE=ON -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DCMAKE_BUILD_TYPE=Release
ninja
# Clean up the dirty hacks
diff --git a/.travis/linux/docker.sh b/.travis/linux/docker.sh
index 3a9970384..166fb6d4c 100755
--- a/.travis/linux/docker.sh
+++ b/.travis/linux/docker.sh
@@ -3,7 +3,7 @@
cd /yuzu
mkdir build && cd build
-cmake .. -G Ninja -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
+cmake .. -G Ninja -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DCMAKE_C_COMPILER=/usr/lib/ccache/gcc -DCMAKE_CXX_COMPILER=/usr/lib/ccache/g++ -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DUSE_DISCORD_PRESENCE=ON
ninja
ccache -s
diff --git a/.travis/macos/build.sh b/.travis/macos/build.sh
index 0abd1a93a..db1c7cae7 100755
--- a/.travis/macos/build.sh
+++ b/.travis/macos/build.sh
@@ -4,13 +4,12 @@ set -o pipefail
export MACOSX_DEPLOYMENT_TARGET=10.14
export Qt5_DIR=$(brew --prefix)/opt/qt5
-export UNICORNDIR=$(pwd)/externals/unicorn
export PATH="/usr/local/opt/ccache/libexec:$PATH"
# TODO: Build using ninja instead of make
mkdir build && cd build
cmake --version
-cmake .. -DYUZU_USE_BUNDLED_UNICORN=ON -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
+cmake .. -DYUZU_USE_QT_WEB_ENGINE=ON -DCMAKE_BUILD_TYPE=Release -DENABLE_COMPATIBILITY_LIST_DOWNLOAD=ON -DYUZU_ENABLE_COMPATIBILITY_REPORTING=${ENABLE_COMPATIBILITY_REPORTING:-"OFF"} -DUSE_DISCORD_PRESENCE=ON
make -j4
ccache -s
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2f3c59c5d..273260378 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -18,8 +18,6 @@ CMAKE_DEPENDENT_OPTION(YUZU_USE_BUNDLED_QT "Download bundled Qt binaries" ON "EN
option(ENABLE_WEB_SERVICE "Enable web services (telemetry, etc.)" ON)
-option(YUZU_USE_BUNDLED_UNICORN "Build/Download bundled Unicorn" ON)
-
option(YUZU_USE_QT_WEB_ENGINE "Use QtWebEngine for web applet implementation" OFF)
option(YUZU_ENABLE_BOXCAT "Enable the Boxcat service, a yuzu high-level implementation of BCAT" ON)
@@ -161,7 +159,7 @@ macro(yuzu_find_packages)
# Cmake Pkg Prefix Version Conan Pkg
"Boost 1.73 boost/1.73.0"
"Catch2 2.13 catch2/2.13.0"
- "fmt 7.0 fmt/7.0.3"
+ "fmt 7.1 fmt/7.1.2"
# can't use until https://github.com/bincrafters/community/issues/1173
#"libzip 1.5 libzip/1.5.2@bincrafters/stable"
"lz4 1.8 lz4/1.9.2"
@@ -210,7 +208,7 @@ if(ENABLE_QT)
set(QT_PREFIX_HINT)
if(YUZU_USE_BUNDLED_QT)
if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
- set(QT_VER qt-5.12.0-msvc2017_64)
+ set(QT_VER qt-5.12.8-msvc2017_64)
else()
message(FATAL_ERROR "No bundled Qt binaries for your toolchain. Disable YUZU_USE_BUNDLED_QT and provide your own.")
endif()
@@ -263,6 +261,7 @@ if (CONAN_REQUIRED_LIBS)
libzip:with_openssl=False
libzip:enable_windows_crypto=False
)
+
conan_check(VERSION 1.24.0 REQUIRED)
# Add the bincrafters remote
conan_add_remote(NAME bincrafters
@@ -354,84 +353,22 @@ if (NOT LIBUSB_FOUND)
set(LIBUSB_LIBRARIES usb)
endif()
-# Prefer the -pthread flag on Linux.
-set(THREADS_PREFER_PTHREAD_FLAG ON)
-find_package(Threads REQUIRED)
-
-# If unicorn isn't found, msvc -> download bundled unicorn; everyone else -> build external
-if (YUZU_USE_BUNDLED_UNICORN)
- if (MSVC)
- message(STATUS "unicorn not found, falling back to bundled")
- # Detect toolchain and platform
- if ((MSVC_VERSION GREATER_EQUAL 1910 AND MSVC_VERSION LESS 1930) AND ARCHITECTURE_x86_64)
- set(UNICORN_VER "unicorn-yuzu")
- else()
- message(FATAL_ERROR "No bundled Unicorn binaries for your toolchain. Disable YUZU_USE_BUNDLED_UNICORN and provide your own.")
- endif()
-
- if (DEFINED UNICORN_VER)
- download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
- endif()
-
- if (DEFINED UNICORN_VER)
- download_bundled_external("unicorn/" ${UNICORN_VER} UNICORN_PREFIX)
- endif()
-
- set(UNICORN_FOUND YES)
- set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
- set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/lib/x64/unicorn_dynload.lib" CACHE PATH "Path to Unicorn library" FORCE)
- set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/lib/x64/" CACHE PATH "Path to unicorn.dll" FORCE)
- else()
- message(STATUS "unicorn not found, falling back to externals")
- if (MINGW)
- set(UNICORN_LIB_NAME "unicorn.a")
- else()
- set(UNICORN_LIB_NAME "libunicorn.a")
- endif()
-
- set(UNICORN_FOUND YES)
- set(UNICORN_PREFIX ${PROJECT_SOURCE_DIR}/externals/unicorn)
- set(LIBUNICORN_LIBRARY "${UNICORN_PREFIX}/${UNICORN_LIB_NAME}" CACHE PATH "Path to Unicorn library" FORCE)
- set(LIBUNICORN_INCLUDE_DIR "${UNICORN_PREFIX}/include" CACHE PATH "Path to Unicorn headers" FORCE)
- set(UNICORN_DLL_DIR "${UNICORN_PREFIX}/" CACHE PATH "Path to unicorn dynamic library" FORCE)
-
- find_package(PythonInterp 2.7 REQUIRED)
-
- if (MINGW)
- # Intentionally call the unicorn makefile directly instead of using make.sh so that we can override the
- # UNAME_S makefile variable to MINGW. This way we don't have to hack at the uname binary to build
- # Additionally, overriding DO_WINDOWS_EXPORT prevents unicorn from patching the static unicorn.a by using msvc and cmd,
- # which are both things we don't have in a mingw cross compiling environment.
- add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
- COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" CC=x86_64-w64-mingw32-gcc AR=x86_64-w64-mingw32-gcc-ar RANLIB=x86_64-w64-mingw32-gcc-ranlib make UNAME_S=MINGW DO_WINDOWS_EXPORT=0
- WORKING_DIRECTORY ${UNICORN_PREFIX}
- )
- else()
- add_custom_command(OUTPUT ${LIBUNICORN_LIBRARY}
- COMMAND ${CMAKE_COMMAND} -E env UNICORN_ARCHS="aarch64" PYTHON="${PYTHON_EXECUTABLE}" /bin/sh make.sh macos-universal-no
- WORKING_DIRECTORY ${UNICORN_PREFIX}
- )
- endif()
-
- # ALL makes this custom target build every time
- # but it won't actually build if LIBUNICORN_LIBRARY is up to date
- add_custom_target(unicorn-build ALL
- DEPENDS ${LIBUNICORN_LIBRARY}
- )
- unset(UNICORN_LIB_NAME)
- endif()
+# Use system installed ffmpeg.
+if (NOT MSVC)
+ find_package(FFmpeg REQUIRED)
else()
- find_package(Unicorn REQUIRED)
+ set(FFMPEG_EXT_NAME "ffmpeg-4.2.1")
+ set(FFMPEG_PATH "${CMAKE_BINARY_DIR}/externals/${FFMPEG_EXT_NAME}")
+ download_bundled_external("ffmpeg/" ${FFMPEG_EXT_NAME} "")
+ set(FFMPEG_FOUND YES)
+ set(FFMPEG_INCLUDE_DIR "${FFMPEG_PATH}/include" CACHE PATH "Path to FFmpeg headers" FORCE)
+ set(FFMPEG_LIBRARY_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg library" FORCE)
+ set(FFMPEG_DLL_DIR "${FFMPEG_PATH}/bin" CACHE PATH "Path to FFmpeg dll's" FORCE)
endif()
-if (UNICORN_FOUND)
- add_library(unicorn INTERFACE)
- add_dependencies(unicorn unicorn-build)
- target_link_libraries(unicorn INTERFACE "${LIBUNICORN_LIBRARY}")
- target_include_directories(unicorn INTERFACE "${LIBUNICORN_INCLUDE_DIR}")
-else()
- message(FATAL_ERROR "Could not find or build unicorn which is required.")
-endif()
+# Prefer the -pthread flag on Linux.
+set(THREADS_PREFER_PTHREAD_FLAG ON)
+find_package(Threads REQUIRED)
# Platform-specific library requirements
# ======================================
diff --git a/CMakeModules/CopyYuzuFFmpegDeps.cmake b/CMakeModules/CopyYuzuFFmpegDeps.cmake
new file mode 100644
index 000000000..cca1eeeab
--- /dev/null
+++ b/CMakeModules/CopyYuzuFFmpegDeps.cmake
@@ -0,0 +1,10 @@
+function(copy_yuzu_FFmpeg_deps target_dir)
+ include(WindowsCopyFiles)
+ set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
+ windows_copy_files(${target_dir} ${FFMPEG_DLL_DIR} ${DLL_DEST}
+ avcodec-58.dll
+ avutil-56.dll
+ swresample-3.dll
+ swscale-5.dll
+ )
+endfunction(copy_yuzu_FFmpeg_deps)
diff --git a/CMakeModules/CopyYuzuUnicornDeps.cmake b/CMakeModules/CopyYuzuUnicornDeps.cmake
deleted file mode 100644
index 7af0ef023..000000000
--- a/CMakeModules/CopyYuzuUnicornDeps.cmake
+++ /dev/null
@@ -1,9 +0,0 @@
-function(copy_yuzu_unicorn_deps target_dir)
- include(WindowsCopyFiles)
- set(DLL_DEST "${CMAKE_BINARY_DIR}/bin/$<CONFIG>/")
- windows_copy_files(${target_dir} ${UNICORN_DLL_DIR} ${DLL_DEST}
- libgcc_s_seh-1.dll
- libwinpthread-1.dll
- unicorn.dll
- )
-endfunction(copy_yuzu_unicorn_deps)
diff --git a/dist/icons/controller/applet_dual_joycon.png b/dist/icons/controller/applet_dual_joycon.png
new file mode 100644
index 000000000..32e0a04ae
--- /dev/null
+++ b/dist/icons/controller/applet_dual_joycon.png
Binary files differ
diff --git a/dist/icons/controller/applet_dual_joycon_dark.png b/dist/icons/controller/applet_dual_joycon_dark.png
new file mode 100644
index 000000000..6adc66356
--- /dev/null
+++ b/dist/icons/controller/applet_dual_joycon_dark.png
Binary files differ
diff --git a/dist/icons/controller/applet_dual_joycon_dark_disabled.png b/dist/icons/controller/applet_dual_joycon_dark_disabled.png
new file mode 100644
index 000000000..208603ee7
--- /dev/null
+++ b/dist/icons/controller/applet_dual_joycon_dark_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_dual_joycon_disabled.png b/dist/icons/controller/applet_dual_joycon_disabled.png
new file mode 100644
index 000000000..43950618d
--- /dev/null
+++ b/dist/icons/controller/applet_dual_joycon_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_dual_joycon_midnight.png b/dist/icons/controller/applet_dual_joycon_midnight.png
new file mode 100644
index 000000000..c7edea8a3
--- /dev/null
+++ b/dist/icons/controller/applet_dual_joycon_midnight.png
Binary files differ
diff --git a/dist/icons/controller/applet_dual_joycon_midnight_disabled.png b/dist/icons/controller/applet_dual_joycon_midnight_disabled.png
new file mode 100644
index 000000000..ee1aafc85
--- /dev/null
+++ b/dist/icons/controller/applet_dual_joycon_midnight_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_handheld.png b/dist/icons/controller/applet_handheld.png
new file mode 100644
index 000000000..7f8dd2227
--- /dev/null
+++ b/dist/icons/controller/applet_handheld.png
Binary files differ
diff --git a/dist/icons/controller/applet_handheld_dark.png b/dist/icons/controller/applet_handheld_dark.png
new file mode 100644
index 000000000..41f6d7aea
--- /dev/null
+++ b/dist/icons/controller/applet_handheld_dark.png
Binary files differ
diff --git a/dist/icons/controller/applet_handheld_dark_disabled.png b/dist/icons/controller/applet_handheld_dark_disabled.png
new file mode 100644
index 000000000..5a136ddd1
--- /dev/null
+++ b/dist/icons/controller/applet_handheld_dark_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_handheld_disabled.png b/dist/icons/controller/applet_handheld_disabled.png
new file mode 100644
index 000000000..53f8ce7ad
--- /dev/null
+++ b/dist/icons/controller/applet_handheld_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_handheld_midnight.png b/dist/icons/controller/applet_handheld_midnight.png
new file mode 100644
index 000000000..7188b9154
--- /dev/null
+++ b/dist/icons/controller/applet_handheld_midnight.png
Binary files differ
diff --git a/dist/icons/controller/applet_handheld_midnight_disabled.png b/dist/icons/controller/applet_handheld_midnight_disabled.png
new file mode 100644
index 000000000..6ccfc3253
--- /dev/null
+++ b/dist/icons/controller/applet_handheld_midnight_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_pro_controller.png b/dist/icons/controller/applet_pro_controller.png
new file mode 100644
index 000000000..e32258855
--- /dev/null
+++ b/dist/icons/controller/applet_pro_controller.png
Binary files differ
diff --git a/dist/icons/controller/applet_pro_controller_dark.png b/dist/icons/controller/applet_pro_controller_dark.png
new file mode 100644
index 000000000..c122b7b11
--- /dev/null
+++ b/dist/icons/controller/applet_pro_controller_dark.png
Binary files differ
diff --git a/dist/icons/controller/applet_pro_controller_dark_disabled.png b/dist/icons/controller/applet_pro_controller_dark_disabled.png
new file mode 100644
index 000000000..416e1e2fb
--- /dev/null
+++ b/dist/icons/controller/applet_pro_controller_dark_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_pro_controller_disabled.png b/dist/icons/controller/applet_pro_controller_disabled.png
new file mode 100644
index 000000000..72a549ea9
--- /dev/null
+++ b/dist/icons/controller/applet_pro_controller_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_pro_controller_midnight.png b/dist/icons/controller/applet_pro_controller_midnight.png
new file mode 100644
index 000000000..622e57fa1
--- /dev/null
+++ b/dist/icons/controller/applet_pro_controller_midnight.png
Binary files differ
diff --git a/dist/icons/controller/applet_pro_controller_midnight_disabled.png b/dist/icons/controller/applet_pro_controller_midnight_disabled.png
new file mode 100644
index 000000000..2907f3be4
--- /dev/null
+++ b/dist/icons/controller/applet_pro_controller_midnight_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_left.png b/dist/icons/controller/applet_single_joycon_left.png
new file mode 100644
index 000000000..47c44d43a
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_left.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_left_dark.png b/dist/icons/controller/applet_single_joycon_left_dark.png
new file mode 100644
index 000000000..69530b69c
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_left_dark.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_left_dark_disabled.png b/dist/icons/controller/applet_single_joycon_left_dark_disabled.png
new file mode 100644
index 000000000..cfe4b1475
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_left_dark_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_left_disabled.png b/dist/icons/controller/applet_single_joycon_left_disabled.png
new file mode 100644
index 000000000..c0102dc20
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_left_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_left_midnight.png b/dist/icons/controller/applet_single_joycon_left_midnight.png
new file mode 100644
index 000000000..56522bccb
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_left_midnight.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_left_midnight_disabled.png b/dist/icons/controller/applet_single_joycon_left_midnight_disabled.png
new file mode 100644
index 000000000..62434c188
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_left_midnight_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_right.png b/dist/icons/controller/applet_single_joycon_right.png
new file mode 100644
index 000000000..b0d4fba90
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_right.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_right_dark.png b/dist/icons/controller/applet_single_joycon_right_dark.png
new file mode 100644
index 000000000..af26cce4b
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_right_dark.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_right_dark_disabled.png b/dist/icons/controller/applet_single_joycon_right_dark_disabled.png
new file mode 100644
index 000000000..b33efab42
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_right_dark_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_right_disabled.png b/dist/icons/controller/applet_single_joycon_right_disabled.png
new file mode 100644
index 000000000..01ca38322
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_right_disabled.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_right_midnight.png b/dist/icons/controller/applet_single_joycon_right_midnight.png
new file mode 100644
index 000000000..5bf70e21e
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_right_midnight.png
Binary files differ
diff --git a/dist/icons/controller/applet_single_joycon_right_midnight_disabled.png b/dist/icons/controller/applet_single_joycon_right_midnight_disabled.png
new file mode 100644
index 000000000..e6693b027
--- /dev/null
+++ b/dist/icons/controller/applet_single_joycon_right_midnight_disabled.png
Binary files differ
diff --git a/dist/icons/controller/controller.qrc b/dist/icons/controller/controller.qrc
new file mode 100644
index 000000000..1c4e960c0
--- /dev/null
+++ b/dist/icons/controller/controller.qrc
@@ -0,0 +1,55 @@
+<RCC>
+ <qresource prefix="controller">
+ <file alias="dual_joycon">dual_joycon.png</file>
+ <file alias="dual_joycon_dark">dual_joycon_dark.png</file>
+ <file alias="dual_joycon_midnight">dual_joycon_midnight.png</file>
+ <file alias="handheld">handheld.png</file>
+ <file alias="handheld_dark">handheld_dark.png</file>
+ <file alias="handheld_midnight">handheld_midnight.png</file>
+ <file alias="pro_controller">pro_controller.png</file>
+ <file alias="pro_controller_dark">pro_controller_dark.png</file>
+ <file alias="pro_controller_midnight">pro_controller_midnight.png</file>
+ <file alias="single_joycon_left">single_joycon_left.png</file>
+ <file alias="single_joycon_left_dark">single_joycon_left_dark.png</file>
+ <file alias="single_joycon_left_midnight">single_joycon_left_midnight.png</file>
+ <file alias="single_joycon_right">single_joycon_right.png</file>
+ <file alias="single_joycon_right_dark">single_joycon_right_dark.png</file>
+ <file alias="single_joycon_right_midnight">single_joycon_right_midnight.png</file>
+ <file alias="single_joycon_left_vertical">single_joycon_left_vertical.png</file>
+ <file alias="single_joycon_left_vertical_dark">single_joycon_left_vertical_dark.png</file>
+ <file alias="single_joycon_left_vertical_midnight">single_joycon_left_vertical_midnight.png</file>
+ <file alias="single_joycon_right_vertical">single_joycon_right_vertical.png</file>
+ <file alias="single_joycon_right_vertical_dark">single_joycon_right_vertical_dark.png</file>
+ <file alias="single_joycon_right_vertical_midnight">single_joycon_right_vertical_midnight.png</file>
+ <file alias="applet_dual_joycon">applet_dual_joycon.png</file>
+ <file alias="applet_dual_joycon_dark">applet_dual_joycon_dark.png</file>
+ <file alias="applet_dual_joycon_midnight">applet_dual_joycon_midnight.png</file>
+ <file alias="applet_handheld">applet_handheld.png</file>
+ <file alias="applet_handheld_dark">applet_handheld_dark.png</file>
+ <file alias="applet_handheld_midnight">applet_handheld_midnight.png</file>
+ <file alias="applet_pro_controller">applet_pro_controller.png</file>
+ <file alias="applet_pro_controller_dark">applet_pro_controller_dark.png</file>
+ <file alias="applet_pro_controller_midnight">applet_pro_controller_midnight.png</file>
+ <file alias="applet_joycon_left">applet_single_joycon_left.png</file>
+ <file alias="applet_joycon_left_dark">applet_single_joycon_left_dark.png</file>
+ <file alias="applet_joycon_left_midnight">applet_single_joycon_left_midnight.png</file>
+ <file alias="applet_joycon_right">applet_single_joycon_right.png</file>
+ <file alias="applet_joycon_right_dark">applet_single_joycon_right_dark.png</file>
+ <file alias="applet_joycon_right_midnight">applet_single_joycon_right_midnight.png</file>
+ <file alias="applet_dual_joycon_disabled">applet_dual_joycon_disabled.png</file>
+ <file alias="applet_dual_joycon_dark_disabled">applet_dual_joycon_dark_disabled.png</file>
+ <file alias="applet_dual_joycon_midnight_disabled">applet_dual_joycon_midnight_disabled.png</file>
+ <file alias="applet_handheld_disabled">applet_handheld_disabled.png</file>
+ <file alias="applet_handheld_dark_disabled">applet_handheld_dark_disabled.png</file>
+ <file alias="applet_handheld_midnight_disabled">applet_handheld_midnight_disabled.png</file>
+ <file alias="applet_pro_controller_disabled">applet_pro_controller_disabled.png</file>
+ <file alias="applet_pro_controller_dark_disabled">applet_pro_controller_dark_disabled.png</file>
+ <file alias="applet_pro_controller_midnight_disabled">applet_pro_controller_midnight_disabled.png</file>
+ <file alias="applet_joycon_left_disabled">applet_single_joycon_left_disabled.png</file>
+ <file alias="applet_joycon_left_dark_disabled">applet_single_joycon_left_dark_disabled.png</file>
+ <file alias="applet_joycon_left_midnight_disabled">applet_single_joycon_left_midnight_disabled.png</file>
+ <file alias="applet_joycon_right_disabled">applet_single_joycon_right_disabled.png</file>
+ <file alias="applet_joycon_right_dark_disabled">applet_single_joycon_right_dark_disabled.png</file>
+ <file alias="applet_joycon_right_midnight_disabled">applet_single_joycon_right_midnight_disabled.png</file>
+ </qresource>
+</RCC>
diff --git a/dist/icons/controller/dual_joycon.png b/dist/icons/controller/dual_joycon.png
new file mode 100644
index 000000000..4230f5f7b
--- /dev/null
+++ b/dist/icons/controller/dual_joycon.png
Binary files differ
diff --git a/dist/icons/controller/dual_joycon_dark.png b/dist/icons/controller/dual_joycon_dark.png
new file mode 100644
index 000000000..4445db489
--- /dev/null
+++ b/dist/icons/controller/dual_joycon_dark.png
Binary files differ
diff --git a/dist/icons/controller/dual_joycon_midnight.png b/dist/icons/controller/dual_joycon_midnight.png
new file mode 100644
index 000000000..aac8e5321
--- /dev/null
+++ b/dist/icons/controller/dual_joycon_midnight.png
Binary files differ
diff --git a/dist/icons/controller/handheld.png b/dist/icons/controller/handheld.png
new file mode 100644
index 000000000..d009b4a47
--- /dev/null
+++ b/dist/icons/controller/handheld.png
Binary files differ
diff --git a/dist/icons/controller/handheld_dark.png b/dist/icons/controller/handheld_dark.png
new file mode 100644
index 000000000..c80ca9259
--- /dev/null
+++ b/dist/icons/controller/handheld_dark.png
Binary files differ
diff --git a/dist/icons/controller/handheld_midnight.png b/dist/icons/controller/handheld_midnight.png
new file mode 100644
index 000000000..19de4629b
--- /dev/null
+++ b/dist/icons/controller/handheld_midnight.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller.png b/dist/icons/controller/pro_controller.png
new file mode 100644
index 000000000..07d65e94a
--- /dev/null
+++ b/dist/icons/controller/pro_controller.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller_dark.png b/dist/icons/controller/pro_controller_dark.png
new file mode 100644
index 000000000..73efe18f4
--- /dev/null
+++ b/dist/icons/controller/pro_controller_dark.png
Binary files differ
diff --git a/dist/icons/controller/pro_controller_midnight.png b/dist/icons/controller/pro_controller_midnight.png
new file mode 100644
index 000000000..8d7e63f0d
--- /dev/null
+++ b/dist/icons/controller/pro_controller_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left.png b/dist/icons/controller/single_joycon_left.png
new file mode 100644
index 000000000..547153034
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_dark.png b/dist/icons/controller/single_joycon_left_dark.png
new file mode 100644
index 000000000..b6ee073cb
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_midnight.png b/dist/icons/controller/single_joycon_left_midnight.png
new file mode 100644
index 000000000..34a485c81
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical.png b/dist/icons/controller/single_joycon_left_vertical.png
new file mode 100644
index 000000000..1e6282ad8
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical_dark.png b/dist/icons/controller/single_joycon_left_vertical_dark.png
new file mode 100644
index 000000000..a615d995d
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_left_vertical_midnight.png b/dist/icons/controller/single_joycon_left_vertical_midnight.png
new file mode 100644
index 000000000..4cc578216
--- /dev/null
+++ b/dist/icons/controller/single_joycon_left_vertical_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right.png b/dist/icons/controller/single_joycon_right.png
new file mode 100644
index 000000000..8d29173f6
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_dark.png b/dist/icons/controller/single_joycon_right_dark.png
new file mode 100644
index 000000000..ead2c44e0
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_midnight.png b/dist/icons/controller/single_joycon_right_midnight.png
new file mode 100644
index 000000000..89afe022d
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_midnight.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical.png b/dist/icons/controller/single_joycon_right_vertical.png
new file mode 100644
index 000000000..4d7d06547
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical_dark.png b/dist/icons/controller/single_joycon_right_vertical_dark.png
new file mode 100644
index 000000000..9a6eb3013
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical_dark.png
Binary files differ
diff --git a/dist/icons/controller/single_joycon_right_vertical_midnight.png b/dist/icons/controller/single_joycon_right_vertical_midnight.png
new file mode 100644
index 000000000..685249b68
--- /dev/null
+++ b/dist/icons/controller/single_joycon_right_vertical_midnight.png
Binary files differ
diff --git a/dist/languages/de.ts b/dist/languages/de.ts
new file mode 100644
index 000000000..b93c9f51c
--- /dev/null
+++ b/dist/languages/de.ts
@@ -0,0 +1,4749 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="de" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>Über yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu ist ein experimenteller, quelloffener Emulator für Nintendo Switch, lizensiert unter GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Diese Software sollte nicht dazu verwendet werden, Spiele zu spielen, die du nicht legal erhalten hast.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Webseite&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Quellcode&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Mitwirkende&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Lizenz&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; ist ein Warenzeichen von Nintendo. yuzu ist in keiner Weise mit Nintendo verbunden.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>Verbindung mit dem Server wird hergestellt...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>Abbrechen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation>Tippe auf die obere linke Ecke &lt;br&gt;deines Touchpads.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation>Tippe auf die untere rechte Ecke &lt;br&gt;deines Touchpads.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>Konfiguration abgeschlossen!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Kompatibilität melden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Kompatibilität des Spiels melden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Solltest du einen Bericht zur &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu-Kompatibilitätsliste&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt; beitragen wollen, werden die folgenden Informationen gesammelt und auf der yuzu-Webseite dargestellt:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware-Informationen (CPU / GPU / Betriebssystem)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Welche yuzu-Version du benutzt&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Den verbundenen yuzu-Account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Perfekt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Das Spiel funktioniert einwandfrei und ohne Audio- oder Grafikfehler.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Gut</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Das Spiel funktioniert mit kleineren Grafik- oder Audiofehlern und ist von Anfang bis Ende spielbar. Eventuell sind einige Workarounds erforderlich.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>Okay</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Das Spiel funktioniert mit größeren Grafik- oder Audiofehlern, lässt sich aber mit Workarounds bis zum Ende durchspielen. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Schlecht</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Spiel funktioniert zwar, aber nur mit starken Grafik- oder Audiofehlern. Manche Bereiche des Spiels können selbst mit Workarounds nicht abgeschlossen werden.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menü</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Das Spiel ist wegen schwerwiegendsten Grafik- oder Audiofehlern unspielbar. Das Spiel lässt sich lediglich starten.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Startet nicht</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Das Spiel stürzt beim Versuch zu starten ab.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Unabhängig von Geschwindigkeit oder Performance, wie gut läuft das Spiel von Start bis Ende in dieser Version von yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Vielen Dank für deinen Beitrag!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Absenden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Kommunikationsfehler</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Beim Senden des Berichtes ist ein Fehler aufgetreten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Weiter</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Ausgabe-Engine:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Dieser Nachbearbeitungseffekt passt die Audiogeschwindigkeit an die Emulationsgeschwindigkeit an und verhindert Audioaussetzer. Dies erhöht jedoch die Audioverzögerung.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Audiodehnung aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Audiogerät:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation>Globale Lautstärke verwenden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>Lautstärke:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Lautstärke:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>Allgemeines</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation>Genauigkeit der Emulation:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation>Akkurat</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>Unsicher</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>Debug-Modus aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation>Wir empfehlen, dies auf &quot;Akkurat&quot; zu stellen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation>Unsichere CPU-Optimierungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation>Diese Optionen reduzieren die Genauigkeit, können jedoch die Geschwindigkeit erhöhen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation>Unfuse FMA (erhöht Leistung auf CPUs ohne FMA)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Diese Option steigert die Geschwindigkeit, indem die Genauigkeit von sog. &quot;fused-multiply-add&quot; Anweisungen auf CPUs ohne native FMA-Unterstützung reduziert wird.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation>Schnelleres FRSQRTE und FRECPE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Diese Option steigert die Geschwindigkeit von einigen &quot;floating point&quot;-Anweisungen, indem weniger akkurate Schätzungen der Werte verwendet werden.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Die CPU-Einstellungen sind nur verfügbar, wenn kein Spiel aktiv ist.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation>Debug-Modus für CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation>Der Debug-Modus ist nur für Entwickler gedacht. Bist du dir sicher?</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation>CPU-Optimierungen ändern</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;
+ &lt;b&gt;Dies ist nur zur Fehlerbehebung gedacht .&lt;/b&gt;
+ &lt;br&gt;
+ Falls du dir nicht sicher bist, was hier passiert, solltest du keine dieser Optionen ändern.
+ &lt;br&gt;
+ Diese Optionen treten nur dann in Kraft, wenn auch der CPU Debug-Modus aktiviert ist.
+ &lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation>Enable inline page tables</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation>Enable block linking</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation>Return stack buffer aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation>Enable fast dispatcher</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation>Enable context elimination</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation>Enable constant propagation</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation>Enable miscellaneous optimizations</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation>Enable misalignment check reduction</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Die CPU-Einstellungen sind nur verfügbar, wenn kein Spiel aktiv ist.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>GDB-Stub aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Port:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Logging</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Globaler Log-Filter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Log-Konsole öffnen (nur Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Log-Verzeichnis öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>String-Argumente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation>Grafik</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation>Wenn aktiviert, wird die Grafik-API in einem langsamen Debug-Modus gestartet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation>Grafik-Debugging aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation>Diese Option deaktiviert den Macro-JIT-Compiler. Dies wird die Geschwindigkeit verringern.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation>Macro-JIT deaktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation>Speichern</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Ausführliche Service-Fehlerberichte aktivieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Dies wird automatisch beim Schließen von yuzu zurückgesetzt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Erweitert</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Kiosk(Quest)-Modus</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation>Debug-Controller einrichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation>Löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation>Standardwerte</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>yuzu-Konfiguration</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Allgemein</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>Benutzeroberfläche</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Spieleliste</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>System</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Nutzer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Dateisystem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Steuerung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Hotkeys</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation>CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Debug</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Grafik</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation>Erweitert</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation>GraphicsAdvanced</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Services</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Speicherverzeichnisse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>SD-Karte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Gamecard</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Pfad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Eingelegt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Aktuelles Spiel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Patchmanager</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Dekomprimierte NSOs dumpen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>ExeFS dumpen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Mod-Ladeverzeichnis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>Root dumpen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Caching</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Cache-Ordner</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>Metadaten der Spieleliste cachen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Metadaten-Cache zurücksetzen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Emulierten NAND-Ordner auswählen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Emulierten SD-Ordner auswählen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>Gamecard-Pfad auswählen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Dump-Verzeichnis auswählen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Mod-Ladeverzeichnis auswählen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Cache-Ordner auswählen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>Der Metadaten-Cache ist bereits leer.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>Der Vorgang wurde erfolgreich abgeschlossen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>Der Metadaten-Cache konnte nicht gelöscht werden. Er könnte in Gebrauch oder nicht vorhanden sein.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Allgemein</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Geschwindigkeit auf % festlegen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation>Multicore-CPU-Emulation</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Schließen des Emulators bestätigen, falls ein Spiel läuft</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>Beim Spielstart nach Nutzer fragen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Emulation im Hintergrund pausieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation>Mauszeiger verstecken</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation>API-Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation>API:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation>Gerät:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation>Grafik-Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Nutze Festplatten-Shader-Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Asynchrone GPU-Emulation verwenden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation>Seitenverhältnis:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation>Standard (16:9)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation>4:3 erzwingen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation>21:9 erzwingen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation>Auf Fenster anpassen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation>Globale Hintergrundfarbe verwenden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation>Hintergrundfarbe:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Hintergrundfarbe:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation>OpenGL GPU</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation>Erweiterte Grafik-Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation>Genauigkeit der Emulation:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation>VSync verhindert Screen-Tearing, aber manche Grafikkarten haben eine schlechtere Leistung, wenn es aktiviert ist. Wenn du keinen Unterschied merkst, lasse es aktiviert.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation>VSync nutzen (Nur OpenGL)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation>Aktivieren dieser Einstellung reduziert Stottern durch Shader. Aktiviert OpenGL Assembly-Shader auf unterstützten Nvidia-Karten (NV_gpu_program5 wird benötigt). Dieses Feature ist experimentell.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation>Nutze Assembly-Shader (experimentell, nur Nvidia OpenGL)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation>Nutze asynchrone Shader-Kompilierung. Dies kann Stottern durch Shader reduzieren. Dieses Feature ist experimentell.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation>Nutze asynchrone Shader-Kompilierung (experimentell)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation>Nutze schnelle GPU-Zeit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation>Anisotrope Filterung:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation>Standard</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation>2x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation>4x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation>8x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation>16x</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Hotkey-Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>Doppelklicke auf eine Tastensequenz, um sie zu ändern.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation>Alle löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation>Standardwerte wiederherstellen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Aktion</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Hotkey</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Kontext</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Tastensequenz bereits belegt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation>Die eingegebene Sequenz ist bereits vergeben an: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation>Standardwerte wiederherstellen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation>Löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation>Die Standard-Sequenz ist bereits vergeben an: %1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation>ConfigureInput</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Spieler 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Spieler 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Spieler 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Spieler 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Spieler 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Spieler 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Spieler 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Spieler 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Erweitert</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation>Konsolenmodus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation>Im Dock</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation>Handheld</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation>Vibration</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation>Bewegung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Konfigurieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation>Controller</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation>2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation>3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation>4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation>5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation>6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation>7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation>8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation>Verbunden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation>Standardwerte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation>Löschen</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Eingabe einrichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation>Joyconfarben</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation>Spieler 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation>Links</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation>L-Taste</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation>Rechts</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation>R-Taste</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation>Spieler 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation>Spieler 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation>Spieler 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation>Spieler 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation>Spieler 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation>Spieler 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation>Spieler 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation>Weiteres</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation>Tastatur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation>Erweitert</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation>Touchscreen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation>Maus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation>Bewegung / Touch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation>Konfigurieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation>Debug Controller</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Eingabe einrichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation>Controller verbinden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation>Pro Controller</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation>Zwei Joycons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation>Linker Joycon</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation>Rechter Joycon</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation>Handheld</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation>Eingabegerät</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation>Alle</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation>Tastatur/Maus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation>Profil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation>Speichern</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation>Neu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation>Löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Linker Analogstick</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation>Hoch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation>Links</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation>Rechts</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation>Runter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation>Gedrückt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation>Modifikator</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation>Radius</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation>Deadzone: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation>Modifikator-Radius: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation>Steuerkreuz</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation>ZL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation>Minus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation>Screenshot</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation>Plus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation>Home</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation>ZR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation>SL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation>SR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Tasten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Rechter Analogstick</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation>Deadzone: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation>Modifikator-Radius: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation>[wartet]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation>Bewegung / Touch einrichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation>Bewegung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation>Quelle für Bewegung:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation>Empfindlichkeit:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation>Touch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation>Quelle für Touch:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation>Kalibrierung:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation>(100, 50) - (1800, 850)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation>Einrichtung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation>Tastenbelegung nutzen:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation>CemuhookUDP Konfiguration</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation>Du kannst alle Cemuhook-kompatiblen UDP-Eingabequellen für Bewegung und Touch verwenden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation>Server:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation>Port:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation>Pad:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation>Pad 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation>Pad 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation>Pad 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation>Pad 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation>Mehr erfahren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation>Testen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation>Maus (Rechtsklick)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation>CemuhookUDP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation>Emulator-Fenster</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Mehr erfahren&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation>Testen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation>Einrichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation>Test erfolgreich</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation>Daten wurden erfolgreich vom Server empfangen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation>Test fehlgeschlagen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation>Konnte keine Daten vom Server empfangen.&lt;br&gt;Prüfe bitte, dass der Server korrekt eingerichtet wurde und dass Adresse und Port korrekt sind.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation>Citra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation>UDP-Test oder Kalibration wird gerade durchgeführt.&lt;br&gt;Bitte warte einen Moment.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Maus einrichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Maustasten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Vorwärts:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Rückwärts:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Links:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Mitte:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Rechts:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation>Standardwerte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[nicht belegt]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Standardwert wiederherstellen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[Taste drücken]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation>Dialog</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation>Info</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation>Name</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation>Titel ID</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation>Dateiname</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation>Format</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation>Größe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation>Entwickler</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation>Add-Ons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation>Allgemeines</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation>System</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation>Grafik</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation>Erw. Grafik</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation>Globale Konfiguration verwenden (%1)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation>Patchname</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation>Version</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Nutzerverwaltung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Aktueller Nutzer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Nutzername</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Bild wählen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Umbenennen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>Die Nutzerverwaltung ist nur verfügbar, wenn kein Spiel aktiv ist.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Nutzername eingeben</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Nutzer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Gib einen Benutzernamen für den neuen Benutzer ein:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Gib einen neuen Nutzernamen ein:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Löschen bestätigen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Du bist dabei, den Nutzer &quot;%1&quot; zu löschen. Bist du dir sicher?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Profilbild wählen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>JPEG Bilddateien (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Fehler beim Löschen des Bildes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Fehler beim Überschreiben des vorherigen Bildes bei: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Fehler beim Löschen der Datei</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Konnte die bestehende Datei &quot;%1&quot; nicht löschen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Fehler beim Erstellen des Ordners für die Profilbilder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Konnte Ordner &quot;%1&quot; nicht erstellen, um Profilbilder zu speichern.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Fehler beim Kopieren des Profilbildes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Das Bild konnte nicht von &quot;%1&quot; nach &quot;%2&quot; kopiert werden</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>BCAT ist Nintendos Methode, Daten an Spiele zu senden, um die Community zu involvieren und zusätzliche Inhalte freizuschalten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>BCAT Backend</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Erfahre mehr über BCAT, Boxcat, und aktuelle Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>Der Boxcat Service ist offline oder du bist nicht mit dem Internet verbunden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Bei der Verarbeitung der Boxcat-Eventdaten ist ein Fehler aufgetreten. Kontaktiere die yuzu-Entwickler.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>Die Version von yuzu, die du verwendest, ist entweder zu neu oder zu alt für den Server. Versuche auf die neueste offizielle Version von yuzu zu updaten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>Es gibt zurzeit keine Events auf Boxcat.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation>Momentane Boxcat-Events</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>Yuzu lädt den neuesten Boxcat-Status...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Systemeinstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation>Region:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation>Auto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation>Standard</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation>CET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation>CST6CDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation>Kuba</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation>EET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation>Ägypten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation>Eire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation>EST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation>EST5EDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation>GB-Eire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation>GMT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation>GMT+0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation>GMT-0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation>GMT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation>Greenwich</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation>Hongkong</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation>HST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation>Island</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation>Iran</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation>Israel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation>Jamaika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation>Japan</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation>Kwajalein</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation>Libyen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation>MET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation>MST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation>MST7MDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation>Navajo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation>NZ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation>NZ-CHAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation>Polen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation>Portugal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation>PRC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation>PST8PDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation>ROC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation>ROK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation>Singapur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation>Türkei</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation>UCT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation>Universal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation>UTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation>W-SU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation>WET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation>Zulu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation>USA</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation>Europa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation>Australien</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation>China</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation>Korea</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation>Taiwan</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation>Zeitzone:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Anmerkung: Diese Einstellung kann überschrieben werden, falls deine Region auf &quot;auto-select&quot; eingestellt ist.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Japanisch (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Englisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Französisch (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Deutsch (German)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Italienisch (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Spanisch (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Chinesisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Koreanisch (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Niederländisch (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Portugiesisch (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Russisch (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Taiwanesisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Britisches Englisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Kanadisches Französisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Lateinamerikanisches Spanisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Vereinfachtes Chinesisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Traditionelles Chinesisch (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>Benutzerdefinierte Echtzeituhr</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Sprache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>RNG Seed</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Stereo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Surround</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>Konsolen ID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Soundausgabe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM yyyy h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Neu generieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>Die Systemeinstellungen sind nur verfügbar, wenn kein Spiel aktiv ist.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Dieser Vorgang wird deine momentane &quot;virtuelle Switch&quot; mit einer Neuen ersetzen. Deine momentane &quot;virtuelle Switch&quot; wird nicht wiederherstellbar sein. Dies könnte einige unerwartete Effekte in manchen Spielen mit sich bringen. Zudem könnte der Prozess fehlschlagen, wenn zu alte Daten verwendet werden. Möchtest du den Vorgang fortsetzen?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Warnung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>Konsolen ID: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation>Touchscreen-Belegung einrichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation>Belegung:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation>Neu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation>Löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation>Umbenennen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation>Klicke das untere Feld um einen Punkt hinzuzufügen und drücke dann eine Taste, die diesen Punkt auslösen soll.
+Ziehe die Punkte mit deiner Maus, um ihre Position zu ändern. Doppelklicke auf die Tabelle, um die Belegung des Punktes zu ändern.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation>Punkt löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation>Knopf</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation>Neues Profil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation>Neuen Namen für das Profil eingeben.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation>Profil löschen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation>Profil &quot;%1&quot; löschen?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation>Profil umbenennen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation>Neuer Name:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation>[Knopf drücken]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Touchscreen einrichten:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Achtung: Die Einstellungen auf dieser Seite haben Einfluss auf yuzus Touchscreen-Emulation. Sie zu ändern könnte zu unerwünschtem Verhalten, wie zu einem Ausfall des Touchscreens, führen. Du solltest sie nur verändern, falls du weißt, was du tust.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Berührungsparameter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Berührungsdurchmesser Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Finger</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Berührungsdurchmesser X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Drehwinkel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Standardwerte wiederherstellen</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>Allgemein</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Anmerkung: Das Ändern der Sprache wird deine Konfiguration speichern.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Sprache der Benutzeroberfläche:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Theme:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Spieleliste</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Add-On Spalte anzeigen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Icongröße:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Zeile 1 Text:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Zeile 2 Text:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation>Screenshots</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation>Frage nach, wo Screenshots gespeichert werden sollen (Nur Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation>Screenshotpfad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation>Screenshotpfad auswählen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Englisch</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>yuzu Web Service</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Mit dem Bereitstellen deines Benutzernamens und Tokens erlaubst du yuzu, zusätzliche Nutzungsdaten zu sammeln. Diese könnten auch Informationen beinhalten, die dich identifizieren könnten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Überprüfen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Registrieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token: </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Nutzername: </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>Was ist mein Token?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Telemetrie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Teile anonyme Nutzungsdaten mit dem yuzu-Team</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Mehr erfahren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>Telemetrie-ID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Neu generieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Discord-Präsenz</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Zeig dein momentanes Spiel in deinem Discord-Status</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Mehr erfahren&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Registrieren&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Was ist mein Token?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>Telemetrie-ID: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Nicht spezifiziert</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>Token nicht verifiziert</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>Token wurde nicht verfiziert. Die Änderungen an deinem Token wurden nicht gespeichert.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Verifizieren...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Verifizierung fehlgeschlagen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Verifizierung fehlgeschlagen. Prüfe ob dein Nutzername und Token richtig eingegeben wurden und ob deine Internetverbindung korrekt funktioniert.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonyme Daten werden gesammelt,&lt;/a&gt; um yuzu zu verbessern.&lt;br/&gt;&lt;br/&gt;Möchstest du deine Nutzungsdaten mit uns teilen?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Telemetrie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Textprüfung fehlgeschlagen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>Lade Web-Applet...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Web-Applet verlassen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Verlassen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Um das Web-Applet zu verlassen, verwende die im Spiel enthaltenen Steuerelemente, wähle die die Option &apos;Web-Applet beenden&apos; in der Menüleiste oder drücke die &apos;Enter&apos;-Taste.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Web-Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Diese Version von yuzu wurde ohne QtWebEngine-Unterstützung kompiliert, was bedeutet, dass yuzu das angeforderte Spielhandbuch oder die Webseite nicht richtig anzeigen kann.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation>Wie viele Shader im Moment kompiliert werden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Derzeitige Emulations-Geschwindigkeit. Werte höher oder niedriger als 100% zeigen, dass die Emulation scheller oder langsamer läuft als auf einer Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Wie viele Bilder pro Sekunde angezeigt werden variiert von Spiel zu Spiel und von Szene zu Szene. </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Zeit, die gebraucht wurde, um einen Switch-Frame zu emulieren, ohne Framelimit oder V-Sync. Für eine Emulation bei voller Geschwindigkeit sollte dieser Wert bei höchstens 16.67ms liegen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation>DOCK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation>ASYNC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation>MEHRKERN</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation>VULKAN</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation>OPENGL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Zuletzt geladene Dateien leeren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Warnung veraltetes Spielformat</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Du nutzt eine entpackte ROM-Ordnerstruktur für dieses Spiel, welches ein veraltetes Format ist und von anderen Formaten wie NCA, NAX, XCI oder NSP überholt wurde. Entpackte ROM-Ordner unterstützen keine Icons, Metadaten oder Updates.&lt;br&gt;&lt;br&gt;&lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;Unser Wiki&lt;/a&gt; enthält eine Erklärung der verschiedenen Formate, die yuzu unterstützt. Diese Nachricht wird nicht noch einmal angezeigt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>ROM konnte nicht geladen werden!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>ROM-Format wird nicht unterstützt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Beim Initialisieren des Video-Kerns ist ein Fehler aufgetreten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>Beim Laden des Video-Kerns trat ein Fehler auf. Bitte prüfe die Log-Dateien auf mögliche Fehlermeldungen. Weitere Informationen: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Wie kann ich eine Log-Datei hochladen?&lt;/a&gt;. Stelle sicher, dass die aktuellsten Grafiktreiber für deine Grafikkarte installiert sind.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation>Fehler beim Laden des ROMs!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Ein unbekannter Fehler ist aufgetreten. Bitte prüfe die Log-Dateien auf mögliche Fehlermeldungen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Start</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Speicherdaten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Mod-Daten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Konnte Verzeichnis %1 nicht öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>Verzeichnis existiert nicht!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Fehler beim Öffnen des transferierbaren Shader-Caches</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>Es existiert kein Shader-Cache für diesen Titel.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation>Inhalte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation>Update</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation>DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation>Eintrag entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation>Installiertes Spiel %1 entfernen?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation>Erfolgreich entfernt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation>Das Spiel wurde entfernt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation>Fehler beim Entfernen von %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation>Das Spiel ist nicht im NAND installiert und kann somit nicht entfernt werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation>Das Update wurde entfernt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation>Es ist kein Update für diesen Titel installiert.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation>Es sind keine DLC für diesen Titel installiert.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation>%1 DLC entfernt. </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation>Transferierbaren Shader-Cache entfernen?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation>Spiel-Einstellungen entfernen?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation>Datei entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation>Fehler beim Entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation>Der transferierbare Shader-Cache wurde entfernt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation>Konnte den transferierbaren Shader-Cache nicht entfernen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation>Fehler beim Entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation>Es existieren keine Spiel-Einstellungen für dieses Spiel.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation>Die Spiel-Einstellungen wurden entfernt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation>Die Spiel-Einstellungen konnten nicht entfernt werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>RomFS-Extraktion fehlgeschlagen!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Das RomFS konnte wegen eines Fehlers oder Abbruchs nicht kopiert werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Komplett</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>Nur Ordnerstruktur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>RomFS Extraktions-Modus auswählen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Bitte wähle, wie das RomFS gespeichert werden soll.&lt;br&gt;&quot;Full&quot; wird alle Dateien des Spiels extrahieren, während &lt;br&gt;&quot;Skeleton&quot; nur die Ordnerstruktur erstellt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>RomFS wird extrahiert...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Abbrechen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>RomFS wurde extrahiert!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>Der Vorgang wurde erfolgreich abgeschlossen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Fehler beim Öffnen von %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Verzeichnis auswählen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Einstellungen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>Spiel-Einstellungen konnten nicht geladen werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Switch-Programme (%1);;Alle Dateien (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Datei laden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Öffne das extrahierte ROM-Verzeichnis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Ungültiges Verzeichnis ausgewählt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>Das Verzeichnis, das du ausgewählt hast, enthält keine &apos;main&apos;-Datei.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation>Installierbares Switch-Programm (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submissions Package (*.nsp);;NX Cartridge Image (*.xci)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation>Dateien installieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Datei &quot;%1&quot; wird installiert...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation>NAND-Installation</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Systemanwendung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Systemarchiv</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Systemanwendungsupdate</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Firmware-Paket (Typ A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Firmware-Paket (Typ B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Spiel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Spiel-Update</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>Spiel-DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Delta-Titel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Wähle den NCA-Installationstyp aus...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Bitte wähle, als was diese NCA installiert werden soll:
+(In den meisten Fällen sollte die Standardeinstellung &apos;Spiel&apos; ausreichen.)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Installation fehlgeschlagen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>Der Titel-Typ, den du für diese NCA ausgewählt hast, ist ungültig.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Datei nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Datei &quot;%1&quot; nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Fortsetzen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Fehleranzeige</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Fehlender yuzu-Account</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Um einen Kompatibilitätsbericht abzuschicken, musst du einen yuzu-Account mit yuzu verbinden.&lt;br&gt;&lt;br/&gt;Um einen yuzu-Account zu verbinden, prüfe die Einstellungen unter Emulation &amp;gt; Konfiguration &amp;gt; Web.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation>Fehler beim Öffnen der URL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation>URL &quot;%1&quot; kann nicht geöffnet werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Amiibo-Datei (%1);; Alle Dateien (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Amiibo laden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Fehler beim Öffnen der Amiibo Datei</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Die Amiibo Datei &quot;%1&quot; konnte nicht zum Lesen geöffnet werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Fehler beim Lesen der Amiibo-Daten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Amiibo-Daten können nicht vollständig gelesen werden. Es wurde erwartet, dass %1 Bytes gelesen werden, es konnten aber nur %2 Bytes gelesen werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Fehler beim Laden der Amiibo-Daten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Amiibo-Daten konnten nicht geladen werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Screenshot aufnehmen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG Bild (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Geschwindigkeit: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Geschwindigkeit: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Spiel: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Frame: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>Das Spiel, dass du versuchst zu spielen, benötigt bestimmte Dateien von deiner Switch-Konsole.&lt;br/&gt;&lt;br/&gt;Um Informationen darüber zu erhalten, wie du diese Dateien von deiner Switch extrahieren kannst, prüfe bitte die folgenden Wiki-Seiten: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;System-Archive und Shared Fonts von einer Switch-Konsole extrahieren&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Willst du zur Spiele-Liste zurückkehren und die Emulation beenden? Das Fortsetzen der Emulation könnte zu Spielfehlern, Abstürzen, beschädigten Speicherdaten und anderen Fehlern führen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>yuzu konnte ein Switch Systemarchiv nicht finden. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>yuzu konnte ein Switch Systemarchiv nicht finden: %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Systemarchiv nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Systemarchiv fehlt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>yuzu konnte die Switch Shared Fonts nicht finden. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Shared Fonts nicht gefunden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Shared Font fehlt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Schwerwiegender Fehler</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>Ein schwerwiegender Fehler ist aufgetreten, bitte prüfe die Log-Dateien auf mögliche Fehlermeldungen. Weitere Informationen: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Wie kann ich eine Log-Datei hochladen&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Willst du zur Spiele-Liste zurückkehren und die Emulation beenden? Das Fortsetzen der Emulation könnte zu Spielfehlern, Abstürzen, beschädigten Speicherdaten und anderen Fehlern führen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Fataler Fehler aufgetreten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Schlüsselableitung bestätigen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Du bist im Begriff, alle Schlüssel neu abzuleiten. Falls du nicht weißt, was das heißt oder was du hier tust, könnte dieser Prozess möglicherweise destruktiv sein. Bitte stelle sicher, dass du wirklich fortfahren willst und optional Sicherungen deiner Daten machst.
+
+Dieser Prozess wird die generierten Schlüsseldateien löschen und die Schlüsselableitung neu starten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation>Fuses fehlen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation> - BOOT0 fehlt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation> - BCPKG2-1-Normal-Main fehlt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation> - PRODINFO fehlt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation>Derivationskomponenten fehlen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation>Einige Komponenten, die yuzu benötigt, um Schlüssel zu generieren, wurden nicht gefunden. &lt;br&gt;Bitte folge &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;dem yuzu Schnellstart-Guide&lt;/a&gt; um alle deine Schlüssel und Spiele zu übertragen.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Schlüssel werden abgeleitet...
+Dies könnte, je nach Leistung deines Systems, bis zu einer Minute dauern.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Schlüsselableitung</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>RomFS wählen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Wähle, welches RomFS du speichern möchtest.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Bist du sicher, dass du yuzu beenden willst?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Bist du sicher, dass du die Emulation stoppen willst? Jeder nicht gespeicherte Fortschritt geht verloren.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>Die derzeit laufende Anwendung hat yuzu aufgefordert, sich nicht zu beenden.
+
+Möchtest du dies umgehen und sie trotzdem beenden?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation>OpenGL nicht verfügbar!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation>yuzu wurde nicht mit OpenGL-Unterstützung kompiliert.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation>Vulkan nicht verfügbar!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation>yuzu wurde nicht mit Vulkan-Unterstützung kompiliert.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation>Fehler beim Initialisieren von OpenGL 4.3!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation>Deine Grafikkarte unterstützt kein OpenGL 4.3, oder du hast nicht den neusten Treiber installiert.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation>Fehler beim Initialisieren von OpenGL!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation>Deine GPU unterstützt anscheinend nicht eine oder mehrere von yuzu benötigte OpenGL-Erweiterungen. Bitte stelle sicher, dass du den neusten Grafiktreiber für deine GPU installiert hast.&lt;br&gt;&lt;br&gt;Nicht unterstützte Erweiterungen:&lt;br&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Name</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Kompatibilität</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Add-ons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Dateityp</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Größe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Spielstand-Verzeichnis öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Mod-Verzeichnis öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Transferierbaren Shader-Cache öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation>Entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation>Installiertes Update entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation>Alle installierten DLCs entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation>Shader-Cache entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation>Spiel-Einstellungen entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation>Alle installierten Inhalte entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>RomFS speichern</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Title-ID in die Zwischenablage kopieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>GameDB-Eintrag öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Eigenschaften</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Unterordner scannen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Spieleverzeichnis entfernen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation>▲ Nach Oben</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation>▼ Nach Unten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Verzeichnis öffnen</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Perfekt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>Das Spiel funktioniert fehlerfrei, ohne Audio- oder Grafikfehler, und sämtliche getestete Funktionalität funktioniert wie vorhergesehen ohne Workarounds.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Gut</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>Das Spiel funktioniert mit kleineren Audio- oder Grafikfehlern und lässt sich bis zum Ende durchspielen.
+Eventuell sind einige Workarounds notwendig.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Okay</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>Das Spiel funktioniert mit größern Audio- oder Grafikfehlern,
+lässt sich aber mit Workarounds bis zum Ende durchspielen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Schlecht</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>Spiel funktioniert zwar, aber nur mit starken Audio- oder Grafikfehlern. Manche Bereiche des Spiels können selbst mit Workarounds nicht abgeschlossen werden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menü</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>Das Spiel ist wegen schwerwiegenden Audio- oder Grafikfehlern unspielbar. Das Spiel lässt sich lediglich starten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Startet nicht</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>Das Spiel stürzt beim Versuch zu starten ab.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Nicht getestet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>Spiel wurde noch nicht getestet.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Doppelklicke, um einen neuen Ordner zur Spieleliste hinzuzufügen.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filter:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Wörter zum Filtern eingeben</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation>Bitte bestätige, dass du diese Dateien installieren willst.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation>Wenn du ein Update oder DLC installierst, wirst du die vorher installierten überschreiben.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation>Installieren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation>Dateien im NAND installieren</translation>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>Shader 387 / 1628 wird geladen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>Shader %v / %m wird geladen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Geschätzte Zeit: 5m 4s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>Lädt...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>Shader %1 / %2 wird geladen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Starten...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Geschätzte Zeit: %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Datei</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Letzte Dateien</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Emulation</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Anzeige</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Debugging</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Werkzeuge</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Hilfe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation>Dateien im NAND installieren...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Datei laden...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Verzeichnis laden...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>S&amp;chließen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pause</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Schlüssel neu initialisieren...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>Über yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Einzelfenster-Modus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Einstellungen...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Dock-Widget-Header anzeigen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Filterleiste anzeigen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Statusleiste anzeigen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation>Fenstergröße zurücksetzen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Vollbild</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Neustart</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Amiibo laden...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Kompatibilität berichten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation>Mods-Seite öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation>Schnellstart-Anleitung öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation>FAQ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>yuzu-Verzeichnis öffnen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Screenshot aufnehmen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation>Spiel-Einstellungen ändern...</translation>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroProfile</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Installierte SD-Titel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Installierte NAND-Titel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Systemtitel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Neues Spieleverzeichnis hinzufügen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Strg</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[nicht gesetzt]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Hat %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Achse %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Taste %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[unbekannt]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation>Klick 0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation>Klick 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation>Klick 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation>Klick 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation>Klick 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation>GC Achse %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation>GC Knopf %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[unbenutzt]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Achse %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation>GC Achse %1</translation>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>Ein Fehler ist aufgetreten.
+Bitte versuche es noch einmal oder kontaktiere den Entwickler der Software.
+
+Fehlercode: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>Ein Fehler ist in %1 bei %2 aufgetreten.
+Bitte versuche es noch einmal oder kontaktiere den Entwickler der Software.
+
+Fehlercode: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>Ein Fehler ist aufgetreten.
+Fehlercode: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Wähle einen Benutzer aus:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Nutzer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Profilauswahl</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Text eingeben:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Software-Tastatur</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Hotkey eingeben</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Stack aufrufen</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>Warten auf Mutex 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>has waiters: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>owner handle: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>Warten auf alle Objekte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>Warten auf eines der folgenden Objekte</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation>[%1]%2 %3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation>von keinem Thread pausiert</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>läuft</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>bereit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>pausiert</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>warten auf HLE Rückgabe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>schläft</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>Warten auf IPC-Antwort</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>Warten auf Objekte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>Warten auf Mutex</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>wartet auf condition variable</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>Warten auf den Adressarbiter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>ruhend</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>tot</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation>PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>ideal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>Kern %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Unbekannter Prozessor %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>Prozessor = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>ideal core = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>Affinitätsmaske = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>Thread-ID = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>Priorität = %1(aktuell) / %2(normal)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>Letzte laufende Ticks = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>nicht auf Mutex warten</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>gewartet von Thread</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Wait Tree</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/es.ts b/dist/languages/es.ts
new file mode 100644
index 000000000..437817561
--- /dev/null
+++ b/dist/languages/es.ts
@@ -0,0 +1,4757 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="es" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>Acerca de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu es un emulador experimental de código abierto de Nintendo Switch licenciado bajo GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;Este software no debe ser utilizado para jugar juegos que no hayas obtenido legalmente.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Sitio web&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Código fuente&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contribuidor&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Licencia&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; es una marca registrada de Nintendo. yuzu no esta afiliado con Nintendo de ninguna manera.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>Comunicando con el servidor...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>Cancelar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation>Toque la esquina superior izquierda&lt;br&gt;de su trackpad.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation>Ahora toque la esquina inferior derecha &lt;br&gt;de su trackpad.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>¡Configuración completa!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Informar de compatibilidad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Informar de compatibilidad del juego</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;SI decides presentar una prueba a la &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Lista de Compatibilidad de yuzu&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, La siguiente información será obtenida y mostrada en el sitio web:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Informacion de Hardware (CPU / GPU / Sistema Operativo)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Qué versión de yuzu estas utilizando&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;La cuenta de yuzu conectada&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Perfecto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;El juego funciona a la perfección sin ningún problema gráfico o de audio.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Excelente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;El juego funciona con fallos gráficos o de audio menores y es jugable desde el principio hasta el final. Puede requerir de soluciones temporales.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>Bien</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;El juego funciona con importantes fallos gráficos o de audio, pero es jugable desde el principio hasta el final con soluciones temporales.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Mal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;El juego funciona, pero tiene errores gráficos o de audio. Imposible progresar en ciertas áreas debido a errores incluso con soluciones temporales.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menú</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;No es posible jugar a este juego debido a importantes errores gráficos o de audio. Es imposible avanzar mas allá de la pantalla de inicio.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>No inicia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;El juego se bloquea al intentar iniciar.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independientemente de la velocidad o del rendimiento, ¿cómo definiría su experiencia con el juego de principio a fin en esta versión de yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Gracias por su contribución.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Enviando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Error de comunicación.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Ha ocurrido un error mientras se mandaba el caso de prueba</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Siguiente</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Motor de Salida:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Este efecto de post-procesado ajusta la velocidad del audio para igualarla a la velocidad de emulación y así prevenir parones en el audio. No obstante, esto aumenta la latencia del audio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Activar extensión del audio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Dispositivo de Audio:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation>Usar el volumen global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>Ajustar volumen:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Volumen:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>General</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation>Precisión: </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation>Preciso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>Impreciso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>Activar el Modo de Depuración</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation>Recomendamos ajustar la precisión a &quot;Preciso&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation>Ajustes del Modo Impreciso de Optimización de la CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation>Estos ajustes reducen la precisión para mejorar el rendimiento.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation>Unfuse FMA (mejorar el rendimiendo en las CPU sin FMA)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta opción mejora el rendimiento al reducir la precisión de las instrucciónes fused-multiply-add en las CPU sin soporte nativo de FMA.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation>Más rápido FRSQRTE y FRECPE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta opción mejora el rendimiento de algunas funciones aproximadas de punto flotante al utilizar aproximaciones nativas menos precisas.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Los ajustes de la CPU sólo se encuentran disponibles cuando no se está ejecutando ningún juego.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation>Ajustando la CPU al Modo de Depuración</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation>El Modo de Depuración de la CPU sólo está destinado al uso de los desarrolladores. ¿Estás seguro de que quieres activar esto?</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation>Cambiar las optimizaciones de la CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;
+ &lt;b&gt;Sólo para la depuración.&lt;/b&gt;
+ &lt;br&gt;
+ Si no está seguro de lo que hacen estas opciónes, manténgalos todos activados.
+ &lt;br&gt;
+ Estos ajustes sólo toman efecto cuando la precisión de la CPU esta en &quot;Modo de Depuración&quot;.
+ &lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation>Activar las tablas de páginas inline.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Esta optimización acelera los accesos a la memoria del programa del invitado.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Activando las inlines accede a PageTable::pointers en código emitido.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Desactivando esto obliga a que todos los accesos a la memoria pasen por las funciones Memory::Read/Memory::Write.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation>Activar el enlace de bloques</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta optimización evita las búsquedas del despachador permitiendo que los bloques básicos emitidos salten directamente a otros bloques básicos si el PC de destino es estático.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation>Activar el buffer de la pila de retorno</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta optimización evita las búsquedas de los despachadores al rastrear las direcciones potenciales de retorno de las instrucciones de BL. Esto se aproxima a lo que sucede con un buffer de la pila de retorno en una CPU real.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation>Activar el despachador rápido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Habilitar un sistema de despacho de dos niveles. Un despachador más rápido escrito en ensamblado tiene un pequeño caché MRU de destinos de salto se utiliza primero. Si eso falla, el despacho vuelve al despacho más lento de C++.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation>Activar la eliminación del contexto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esto habilita una optimización de IR que reduce los accesos innecesarios a la estructura del contexto de la CPU.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation>Activar la propagación constante</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esto habilita optimizaciones de IR que implican una propagación constante.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation>Activar optimizaciones misceláneas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esto habilita optimizaciónes misceláneas IR.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation>Activar la reducción del control de desalineación</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Cuando está activada, una desalineación sólo se activa cuando un acceso cruza el límite de una página.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Cuando está desactivado, se desencadena una desalineación en todos los accesos desalineados.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Los ajustes de la CPU sólo se encuentran disponibles cuando no se está ejecutando ningún juego.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>Activar GDB Stub</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Puerto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Registro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Filtro de Registro Global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Mostrar Consola del Registro (Sólo en Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Abrir Ubicación del Registro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Cadena de Argumentos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation>Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation>Cuando está marcado, la API de gráficos entra en un modo de depuración más lento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation>Activar la Depuración de Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation>Cuando está marcado, se desactiva el compilador de macro Just In Time. Activando esto hace que los juegos se ejecuten más lento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation>Desactivar Macro JIT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation>Volcar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Habilitar Servicios de Reporte Detallados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Estas opciones se restablecerán automáticamente cuando yuzu se cierre.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Avanzado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Modo Quiosco (Quest)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation>Configurar el Control de Depuración</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation>Eliminar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation>Valores Predeterminados</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>Configuración de yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>General</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>IU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Lista de Juegos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Perfiles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Sistema de Archivos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Controles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Teclas de Acceso Rápido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation>CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Depuración</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation>Avanzado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation>GráficosAvanzados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Servicios</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Directorios de Almacenamiento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>Tarjeta SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Cartucho de Juegos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Ruta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Insertado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Juego Actual</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Administrador de Parches</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Volcar NSOs Descomprimidos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>Volcar Partición ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Carpeta raíz de carga de Mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>Carpeta raíz de Volcado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Cargando Caché</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Directorio de Caché</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>Metadatos de Lista de Juegos en Caché</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Reiniciar Caché de Metadatos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Seleccione el directorio de NAND Emulado...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Seleccione el directorio de SD Emulado...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>Seleccione la ruta del Cartucho...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Seleccione Directorio para Volcar...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Seleccione el directorio de carga de Mod...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Seleccione el Directorio para Caché...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>El caché de metadatos ya está vacío.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>La operación se completó con éxito.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>El caché de metadatos no se pudo eliminar. Puede que se encuentre en uso actualmente o ya haya sido eliminado.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>General</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Limitar el Porcentaje de Velocidad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation>Emulación de CPU multinúcleo </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Confirmar salida mientras se ejecuta la emulación</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>Mostrar usuario actual al abrir el juego</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Pausar emulación cuando la ventana esté en segundo plano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation>Ocultar el cursor del ratón cuando no está activo.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation>Ajustes de la API</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation>API:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation>Dispositivo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation>Ajustes de los Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Usar caché de shaders en disco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Usar emulación asíncrona de GPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation>Relación de Aspecto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation>Valor Predeterminado (16:9)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation>Forzar a 4:3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation>Forzar a 21:9</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation>Estirar a la Ventana</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation>Usar el color de fondo global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation>Establecer el color de fondo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Color de Fondo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation>Dispositivo Gráfico OpenGL:</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation>Ajustes de los Gráficos Avanzados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation>Nivel de Precisión: </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation>VSync evita que la pantalla se desgarre, pero algunas tarjetas gráficas tienen un rendimiento menor con VSync activado. Mantenlo activado si no notas una diferencia de rendimiento.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation>Usar VSync (sólo en OpenGL)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation>Activando esto reduce el tartamudeo causado por la compilación de shaders. Activa shaders de ensamblaje OpenGL en los dispositivos de Nvidia soportados (se requiere NV_gpu_program5). Esta función es experimental.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation>Usar shaders de ensamblaje (experimental, sólo Nvidia OpenGL)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation>Activa la compilación de shaders en modo asíncrono, cuál podría reducir el tartamudeo de los shaders. Esta función es experimental.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation>Usar la construcción de shaders asíncronos (experimental)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation>Usar Tiempo Rápido en la GPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation>Filtrado Anisotrópico:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation>Valor Predeterminado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation>2x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation>4x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation>8x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation>16x</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Configuración de Teclas de Acceso Rápido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>Haz doble-clic para cambiar la asignación de teclas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation>Eliminar Todo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation>Restaurar Valores Predeterminados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Acción</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Tecla de Acceso Rápido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Contexto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Secuencia de Teclas en Conflicto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation>La secuencia de teclas introducida ya ha sido asignada a: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation>Restaurar Valor Predeterminado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation>Eliminar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation>La secuencia de teclas predeterminada ya ha sido asignada a: %1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation>ConfigurarEntrada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Jugador 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Jugador 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Jugador 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Jugador 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Jugador 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Jugador 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Jugador 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Jugador 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Avanzado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation>Modo de la Consola</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation>Estacionado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation>Portátil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation>Vibración</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation>Movimiento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Configurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation>Controladores</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation>2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation>3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation>4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation>5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation>6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation>7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation>8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation>Conectado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation>Valores Predeterminados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation>Eliminar</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configurar Controles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation>Colores de Joycon</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation>Jugador 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation>Lado L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation>Botón L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation>Lado R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation>Botón R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation>Jugador 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation>Jugador 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation>Jugador 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation>Jugador 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation>Jugador 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation>Jugador 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation>Jugador 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation>Otro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation>Teclado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation>Avanzado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation>Pantalla Táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation>Ratón</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation>Movimiento / Táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation>Configurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation>Control de Depuración</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configurar Controles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation>Conectar el Controlador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation>Pro Controller</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation>Doble Joycons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation>Joycon Izquierdo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation>Joycon Derecho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation>Portátil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation>Dispositivo de Entrada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation>Cualquier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation>Teclado/Ratón</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation>Perfil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation>Guardar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation>Crear</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation>Borrar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Palanca Izquierda</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation>Arriba</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation>Izquierda</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation>Derecha</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation>Abajo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation>Presionado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation>Modificador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation>Rango</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation>Zona Muerta: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation>Rango del Modificador: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation>Cruceta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation>ZL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation>Menos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation>Captura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation>Más</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation>Inicio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation>ZR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation>SL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation>SR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Botones Frontales</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Palanca Derecha</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation>Zona Muerta: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation>Rango del Modificador: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation>[esperando]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation>Configurar: Movimiento / Táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation>Movimiento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation>Proveedor de Movimiento:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation>Sensibilidad:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation>Táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation>Proveedor de Táctil:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation>Calibración:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation>(100, 50) - (1800, 850)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation>Configurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation>Usar el mapeo de botones:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation>Configuración CemuhookUDP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation>Puede utilizar cualquier fuente de entrada UDP compatible con Cemuhook para proporcionar una entrada de movimiento y de tacto.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation>Servidor:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation>Puerto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation>Pad:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation>Pad 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation>Pad 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation>Pad 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation>Pad 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation>Más Información</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation>Probar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation>Ratón (Clic Derecho)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation>CemuhookUDP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation>Ventana del Emulador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Más Información&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation>Probando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation>Configurando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation>Prueba Existosa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation>Se recibió con éxito los datos del servidor.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation>Prueba fallida</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation>No pudo recibir datos válidos del servidor.&lt;br&gt;Por favor, verifique que el servidor esté configurado correctamente y que la dirección y el puerto sean correctos.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation>Citra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation>La prueba de UDP o la configuración de la calibración está en curso.&lt;br&gt;Por favor, espere a que termine el proceso.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Configurar el Ratón</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Botones del Ratón</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Adelante:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Atrás:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Izquierda:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Medio:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Derecha:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Borrar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation>Valores Predeterminados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[no establecido]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Restaurar Valor Predeterminado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[pulsa un botón]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation>Diálogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation>Información</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation>ID del Título</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation>Nombre del Archivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation>Versión</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation>Tamaño</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation>Desarrollador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation>Complementos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation>General</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation>Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation>Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation>Gráficos Avanzados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation>Propiedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation>Usar la configuración global (%1)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation>Nombre del Parche</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation>Versión</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Administrador de Perfiles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Usuario Actual</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Nombre de Usuario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Seleccionar Imagen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Añadir</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Renombrar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Eliminar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>El sistema de perfiles solo se encuentra disponible cuando no se está ejecutando ningún juego.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Introduzca el Nombre de Usuario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Usuarios</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Introduzca un nombre para el nuevo usuario:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Introduzca un nuevo nombre de usuario:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation> Confirmar Eliminanación</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Estás a punto de eliminar al usuario &quot;%1&quot; ¿Estás seguro?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Seleccione una Imagen de Usuario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>Imagenes JPEG (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Error al eliminar la imagen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Ocurrió un error al intentar sobrescribir la imagen anterior en: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Error eliminando archivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>No se pudo eliminar el archivo existente: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Error al crear el directorio de imagen del usuario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>No se puede crear el directorio % 1 para almacenar imágenes de usuario.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Error al copiar la imagen del usuario.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>No se puede copiar la imagen de %1 a %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>BCAT es la forma con la que Nintendo envía datos de sus juegos para así interconectar su comunidad y desbloquear contenido adicional.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>Backend de BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;Saber más sobre BCAT, Boxcat, y sus eventos actuales.&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>El servicio boxcat se encuentra fuera de linea o usted no está conectado a internet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Hubo un error al intentar procesar los datos del evento de boxcat. Por favor, contacte con los desarrolladores de yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>La versión de yuzu que está utilizando actualmente es demasiado nueva o demasiado antigua para este servidor. Intente actualizar a la última versión oficial de yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>No hay ningún evento de boxcat en estos momentos.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation>Eventos de Boxcat Actuales</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>Yuzu está verificando el estado actual de boxcat...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Ajustes del sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation>Región:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation>Auto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation>Valor Predeterminado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation>CET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation>CST6CDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation>Cuba</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation>EET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation>Egipto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation>Eire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation>EST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation>EST5EDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation>GB-Eire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation>GMT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation>GMT+0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation>GMT-0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation>GMT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation>Greenwich</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation>Hongkong</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation>HST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation>Islandia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation>Iran</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation>Israel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation>Jamaica</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation>Japón</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation>Kwajalein</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation>Libia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation>MET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation>MST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation>MST7MDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation>Navajo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation>NZ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation>NZ-CHAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation>Polonia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation>Portugal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation>PRC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation>PST8PDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation>ROC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation>ROK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation>Singapur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation>Turquía</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation>UCT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation>Universal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation>UTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation>W-SU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation>WET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation>Zulu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation>EEUU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation>Europa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation>Australia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation>China</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation>Corea</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation>Taiwan</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation>Zona Horaria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Nota: esto puede ser ignorado cuando la opción de región está en &quot;autoseleccionar&quot;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Japonés (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Inglés (english)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Francés (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Alemán (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Italiano (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Español</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Chino</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Coreano (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Holandés (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Portugués (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Ruso (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Taiwanés</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Inglés Británico</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Francés Canadiense</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Español latinoamericano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Chino Simplificado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Chino Tradicional (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>RTC Personalizado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Idioma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>Semilla de GNA</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Estereo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Envolvente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>ID de la Consola:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Modo de salida del sonido:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM aaaa h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Regenerar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>Los ajustes del sistema solo se encuentran disponibles cuando no se está ejecutando ningún juego.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Esto reemplazará tu Switch virtual con una nueva. Tu Switch virtual actual no será recuperable. Esto podría causar efectos inesperados en determinados juegos. Si usas un archivo de configuración obsoleto, esto podría fallar. ¿Continuar?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Advertencia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>ID de Consola: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation>Configurar las Asignaciones de la Pantalla Táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation>Asignaciones:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation>Crear</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation>Borrar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation>Renombrar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation>Haz clic en el área inferior para añadir un punto, y luego presiona un botón para unir.
+Arrastre los puntos para cambiar de posición, o haga doble clic en las celdas de la tabla para editar los valores.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation>Borrar Punto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation>Botón</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation>Crear Perfíl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation>Introduzca un nombre para el nuevo perfíl:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation>Borrar Perfíl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation>Borrar Perfíl %1?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation>Renombrar Perfíl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation>Nuevo nombre:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation>[presionar tecla]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Configurar pantalla táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Advertencia: Los ajustes en esta página afectarán al funcionamiento de la pantalla táctil emulada de yuzu. Cambiarlas podría resultar en un comportamiento inapropiado, como que la pantalla táctil deje de funcionar parcialmente. Se recomienda usar esta pestaña sólo si sabes lo que estás haciendo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Parámetros Táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Diámetro Táctil Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Dedo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Diámetro Táctil X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Ángulo Rotacional</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Recuperar ajustes predeterminados</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>General</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Nota: Cambiar el idioma guardará tu configuración actual.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Lenguage de la Interfaz:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Tema:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Lista de Juegos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Mostrar Columna de Accesorios</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Tamaño de los Iconos:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Texto de la Fila 1:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Texto de la Fila 2:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation>Capturas de Pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation>Pregunte Dónde Guardar las Capturas de Pantalla (sólo para Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation>Trayectoria de las Capturas de Pantalla:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation>Selecciona la Trayectoria de las Capturas de Pantalla:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Inglés</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulario</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>Servicio Web de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Al proporcionar su nombre de usuario y token, das tu consentimiento a que yuzu recopile datos de uso adicionales, que pueden incluir información de identificación del usuario.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Verificar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Registrarse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token: </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Nombre de usuario:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>¿Cuál es mi token?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Telemetría</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Compartir datos de uso anónimos con el equipo de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Saber más</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>ID de Telemetría:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Regenerar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Presencia de Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Mostrar el juego actual en tu estado de Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Saber más&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Regístrate&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;¿Cuál es mi token?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>ID de Telemetría: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Sin especificar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>No se pudo verificar el token</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>El token pudo ser verificado. El cambio realizado a su token no se ha guardado.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Verificando...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Verificación fallida</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Verificación fallida. Compruebe que ha ingresado el token correctamente, y que esté funcionando su conexión a internet.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Se recogen datos anónimos&lt;/a&gt; para ayudar a mejorar yuzu. &lt;br/&gt;&lt;br/&gt;¿Quieres compartir tus datos de uso con nosotros?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Telemetría </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Comprobación de Texto Fallida</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>Cargando Web Applet...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Cerrar Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Salir</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Para salir de la aplicación web, use los controles provistos por el juego y seleccione la opción &quot;Cerrar Web Applet&quot; en la barra del menú, o presione la tecla &quot;Enter&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Esta versión de yuzu fue creada sin soporte para QtWebEngine, lo que significa que yuzu no puede mostrar correctamente el manual del juego o la página solicitada.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation>La cantidad de shaders que se están construyendo actualmente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>La velocidad de emulación actual. Los valores superiores o inferiores al 100% indican que la emulación se está ejecutando más rápido o más lento que en una Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Cuántos fotogramas por segundo está mostrando el juego actualmente. Esto variará de un juego a otro y de una escena a otra.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Tiempo que lleva emular un fotograma de la Switch, sin tener en cuenta la limitación de fotogramas o sincronización vertical. Para una emulación óptima, este valor debería ser como máximo de 16.67 ms.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation>ESTACIONADO</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation>ASINC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation>MULTINÚCLEO</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation>VULKAN</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation>OPENGL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Limpiar Archivos Recientes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Advertencia Formato de Juego Obsoleto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Está utilizando el formato de directorio de ROM deconstruido para este juego, que es un formato desactualizado que ha sido reemplazado por otros, como NCA, NAX, XCI o NSP. Los directorios de ROM deconstruidos carecen de íconos, metadatos y soporte de actualización.&lt;br&gt;&lt;br&gt;Para obtener una explicación de los diversos formatos de Switch que soporta yuzu,&lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;echa un vistazo a nuestra wiki&lt;/a&gt;. Este mensaje no se volverá a mostrar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>¡Error al cargar la ROM!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>El formato de la ROM no es compatible.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Se produjo un error al inicializar el núcleo de video.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu ha encontrado un error al ejecutar el núcleo de video, consulte el registro para obtener más detalles. Para obtener más información sobre cómo acceder al registro, consulte la siguiente página: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Cómo cargar el archivo de registro.&lt;/a&gt; Asegúrese de tener los últimos controladores de gráficos para su GPU.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation>¡Error al cargar la ROM!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Error desconocido. Por favor, consulte el registro para ver más detalles.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Iniciar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Datos de Juegos Guardados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Datos de Mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Error al abrir la carpeta %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>¡La carpeta no existe!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Error al Abrir el Caché Transferible de Shaders</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>No existe un Caché de shaders para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation>Contenidos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation>Actualización</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation>DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation>Eliminar el Título</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation>Eliminar el Juego Instalado %1?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation>Se Eliminó con Éxito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation>Se eliminó con éxito el juego de base instalado.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation>Error en la eliminación de %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation>El juego base no está instalado en el NAND y no se puede eliminar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation>Se eliminó con éxito la actualización instalada.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation>No hay ninguna actualización instalada para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation>No hay ningún DLC instalado para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation>Se eliminó con éxito %1 DLC instalado(s).</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation>¿Desea eliminar el Caché Transferible de Shaders?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation>¿Desea Eliminar la Configuración Personalizada del Juego?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation>Eliminar Archivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation>Error al Eliminar el Caché Transferible de Shaders</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation>El Caché Transferible de Shaders fue eliminado con éxito.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation>No se ha podido eliminar el caché de shaders transferibles.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation>Error al Eliminar la Configuración Personalizada del Juego</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation>No existe una configuración personalizada para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation>Se eliminó con éxito la configuración personalizada del juego.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation>No se ha podido eliminar la configuración personalizada del juego.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>¡La extracción de RomFS falló!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Se produjo un error al copiar los archivos RomFS o el usuario canceló la operación.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Completo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>Base</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Elegir modo para volcar el RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Seleccione la forma en que desea volcar el RomFS. &lt;br&gt;Copiará todos los archivos en el nuevo directorio &lt;br&gt; mientras que el esqueleto solo creará la estructura del directorio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>Extrayendo RomFS...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Cancelar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>¡La extracción RomFS tuvo éxito!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>La operación se completó con éxito.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Error al intentar abrir %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Seleccionar Directorio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Propiedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>No se pudieron cargar las propiedades del juego.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Ejecutable de Switch (%1);;Todos los archivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Cargar archivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Abrir el directorio de la ROM extraída</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Directorio no válido seleccionado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>El directorio que ha seleccionado no contiene ningún archivo &apos;main&apos;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation>Archivo de Switch Instalable (*.nca *.nsp *.xci);;Archivo de Contenidos Nintendo (*.nca);;Paquete de Envío Nintendo (*.nsp);;Imagen de Cartucho NX (*.xci)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation>Instalar Archivos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Instalando el archivo &quot;%1&quot;...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation>Instalar Resultados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Aplicación del sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Archivo del Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Actualización de la aplicación del sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Paquete de Firmware (Tipo A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Paquete de Firmware (Tipo B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Juego</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Actualización del juego</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>DLC del Juego</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Titulo Delta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Seleccione el tipo de instalación NCA...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Seleccione el tipo de título en el que desea instalar este NCA como:
+(En la mayoría de los casos, el &apos;Juego&apos; predeterminado está bien).</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Fallo en la instalación</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>El tipo de título que seleccionó para el NCA no es válido.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Archivo no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Archivo &quot;%1&quot; no encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Continuar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Mostrar Error</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Falta la cuenta de Yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Para enviar un caso de prueba de compatibilidad de juegos, debe vincular su cuenta de yuzu.&lt;br&gt;&lt;br/&gt; Para vincular su cuenta yuzu, vaya a Emulación &amp; gt; Configuración &amp; gt; Web.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation>Error al abrir la URL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation>No se puede abrir la URL &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Archivo Amiibo (%1);; Todos los archivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Cargar amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Error al abrir el archivo de datos de Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>No se puede abrir el archivo de Amiibo &quot;%1&quot; para leer.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Error al leer el archivo de datos de Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>No se pueden leer completamente los datos de Amiibo. Se esperaban leer %1 bytes, pero solo se pudo leer %2 bytes.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Error al cargar los datos de Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>No se pueden cargar los datos de Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Captura de Pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>Imagen PNG (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Velocidad: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Velocidad: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Juego: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Fotogramas: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>El juego que estás intentando cargar requiere de archivos adicionales de su Switch antes de poder jugar. &lt;br/&gt;&lt;br/&gt;Para obtener más información sobre cómo obtener estos archivos, ve a la siguiente página de la wiki: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Volcar archivos del sistema y las fuentes compartidas desde una Consola Switch. &lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;¿Quieres volver a la lista de juegos? Continuar con la emulación puede provocar fallos, datos de guardado dañados u otros errores.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>yuzu no pudo localizar el archivo de sistema de la Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>yuzu no pudo localizar un archivo de sistema de la Switch: %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Archivo del Sistema No Encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Falta Archivo del Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>yuzu no pudo encontrar las Fuentes Compartidas de la Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Fuentes compartidas no encontradas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Faltan las Fuentes Compartidas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Error Fatal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu ha encontrado un error fatal, consulte el registro para obtener más detalles. Para obtener más información sobre cómo acceder al registro, consulte la siguiente página: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;¿Cómo cargar el archivo de registro?&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt; La emulación continua puede provocar fallos, datos de guardado dañados u otros errores.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Error Fatal Encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Confirma la Clave de Rederivación</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Estás a punto de forzar todas tus claves.
+Si no sabes qué es esto,
+es una acción potencialmente destructiva.
+Por favor, asegúrese de que esto
+es lo que desea hacer si es necesario.
+
+ Esto eliminará los archivos de las claves generados automáticamente y volverá a ejecutar el módulo de derivación de claves.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation>Falta fuses</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation>- Falta BOOT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation> - Falta BCPKG2-1-Normal-Main</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation> - Falta PRODINFO</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation>Faltan Componentes de Derivación</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation>Faltan componentes que pueden impedir que la derivación de la clave se complete. &lt;br&gt;Por favor siga &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;la guía de inicio rápido de yuzu&lt;/a&gt; para conseguir todas tus llaves y juegos.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Derivando claves...
+Esto puede llevar unos minutos dependiendo
+del rendimiento de su sistema.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Obtención de claves</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Selecciona el destinatario para volcar el RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Por favor, seleccione los RomFS que desea volcar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>¿Estás seguro de que quieres cerrar yuzu?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>¿Estás seguro de que quieres detener la emulación? Cualquier progreso no guardado se perderá.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>La aplicación que se está ejecutando actualmente ha solicitado que yuzu no sea cerrado.
+
+¿Desea cerrarlo de todas formas?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation>OpenGL no está disponible!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation>yuzu no ha sido compilado con soporte de OpenGL.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation>Vulkan no está disponible!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation>yuzu no ha sido compilado con soporte de Vulkan.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation>¡Error al inicializar OpenGL 4.3!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation>Tu GPU no soporta OpenGL 4.3 o no tienes instalado el último controlador de la tarjeta gráfica.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation>¡Error al inicializar OpenGL!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation>Es posible que su GPU no soporta una o más extensiones OpenGL requeridas. Por favor, asegúrese de tener el último controlador de la tarjeta gráfica&lt;br&gt;&lt;br&gt;Extensiones no soportadas:&lt;br&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Nombre</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Compatibilidad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Complementos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Tipo de Archivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Tamaño</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Abrir ubicación de los archivos de guardado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Abrir ubicación de Mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Abrir Caché Transferible de Shaders</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation>Eliminar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation>Eliminar la Actualización Instalada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation>Eliminar todos los DLC instalados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation>Eliminar el Caché de Shaders</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation>Eliminar Configuración Personalizada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation>Eliminar Todos los Contenidos Instalados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Volcar RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Copiar ID de título al portapapeles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Navegar a la entrada de BD del juego</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Propiedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Escanear subdirectorios</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Eliminar Directorio de Juegos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation>▲ Mover Hacia Arriba</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation>▼ Mover Hacia Abajo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Abrir Ubicación del Directorio</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Perfecto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>El juego funciona a la perfección sin fallos de audio o gráficos, todas las funciones probadas funcionan según lo previsto
+sin ninguna solución necesaria.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Excelente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>El juego funciona con fallos gráficos o de audio menores y se puede jugar de principio a fin. Puede requerir de
+soluciones temporales.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Bien</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>El juego funciona con importantes fallos gráficos o de audio, pero el juego se puede jugar de principio a fin con
+soluciones temporales.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Mal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>El juego funciona, pero con importantes fallos gráficos o de audio. Es imposible avanzar en zonas específicas
+incluso con soluciones temporales.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menú</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>No es posible jugar a este juego debido a importantes errores gráficos o de audio. Es imposible avanzar mas allá de la pantalla
+de inicio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>No inicia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>El juego se bloquea al intentar iniciar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Sin probar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>El juego todavía no ha sido probado.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Haga doble clic para agregar un nuevo directorio a la lista de juegos.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filtrar:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Introduce patrón para filtrar</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation>Por favor, confirme que estos son los archivos que desea instalar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation>Instalando una Actualización o DLC reemplazará la que está instalada anteriormente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation>Instalar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation>Instalar archivos al NAND...</translation>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>Cargando Shaders 387 / 1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>Cargando Shaders %v de %m</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Tiempo Estimado 5m 4s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>Cargando...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>Cargando Shaders %1 / %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Iniciando...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Tiempo estimado %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Archivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Archivos recientes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Emulación</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Ver</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Depuración</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Herramientas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Ayuda</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation>Instalar archivos al NAND...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Cargar archivo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Cargar carpeta...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>S&amp;alir</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Iniciar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pausar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Detener</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Reiniciar claves...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>Acerca de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Modo Ventana Única</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Configurar...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Mostrar los Encabezados del Widget</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Mostrar Barra de Filtro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Mostrar Barra de Estado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation>Reiniciar el Tamaño de la Ventana</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Pantalla completa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Cargar Amiibo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Informar de compatibilidad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation>Abrir la Página de Mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation>Abrir la Guía de Inicio Rápido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation>Preguntas Más Frecuentes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Abrir la carpeta yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Captura de Pantalla</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation>Configurar el Juego Actual...</translation>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroPerfil</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Títulos instalados en la SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Títulos instalados en la NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Títulos del Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Agregar un Nuevo Directorio de Juegos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[no establecido]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Rotación %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Eje %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Botón %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[desconocido]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation>Clic 0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation>Clic 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation>Clic 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation>Clic 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation>Clic 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation>Eje GC %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation>Botón GC %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[no usado]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Eje %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation>Eje GC %1</translation>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>Ha ocurrido un error.
+Inténtelo nuevamente o contacte con el desarrollador del software.
+
+Código del error: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>Ha ocurrido un error en el %1 a las %2.
+Inténtelo nuevamente o contacte con el desarrollador del software.
+
+Código del error: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>Ha ocurrido un error.
+Códio del error: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Seleccione un usuario:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Usuarios</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Selector de perfil</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Introducir texto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Software del Teclado</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Ingrese combinación de teclas de acceso rápido</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Pila de Llamadas</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>esperando por mutex 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>tiene receptores: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>manejo del propietario: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>esperando todos los objetos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>esperando uno de los siguientes objetos</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation>[%1]%2 %3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation>esperado por ningún hilo</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>corriendo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>listo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>en pausa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>esperando para el retorno HLE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>dormido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>esperando para respuesta IPC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>esperando objetos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>esperando por mutex</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>esperando variable condicional</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>esperando al árbitro de dirección</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>inactivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>muerto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation>PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>ideal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>núcleo %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Procesador desconocido %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>procesador = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>núcleo ideal = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>máscara de afinidad = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>id de hilo = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>prioridad = %1(presente) / %2(normal)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>Últimos ticks consecutivos = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>no esperando por mutex</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>esperado por hilo</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Árbol de Espera</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/fr.ts b/dist/languages/fr.ts
new file mode 100644
index 000000000..83e678f93
--- /dev/null
+++ b/dist/languages/fr.ts
@@ -0,0 +1,4732 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="fr" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>À propos de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu est un émulateur expérimental open-source pour la Nintendo Switch licencié sous GPLv2.0. &lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;Ce logiciel ne doit pas être utilisé pour jouer à des jeux que vous n&apos;avez pas obtenu légalement.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;
+&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Site Web&lt;/span&gt;&lt;/a&gt;|&lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Code Source&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributeurs&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Licence&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; est une marque déposée de Nintendo. yuzu n&apos;est en aucun cas affilié à Nintendo.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>Communications avec le serveur...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>Annuler</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation>Touchez le coin supérieur&lt;br&gt;gauche de votre pavé tactile.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation>Touchez le coin supérieur gauche&lt;br&gt; de votre pavé tactile.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>Configuration terminée!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Signaler la compatibilité</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Signaler la compatibilité d&apos;un jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Si vous choisissez à soumettre un test d&apos;essai à la liste de compatibilité yuzu&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, Les informations suivantes seront collectées et publiées sur le site :
+&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Informations Système (Processeur / Carte Graphique / Système d&apos;exploitation)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;La version de yuzu que vous employez&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Le compte yuzu sous lequel vous êtes connecté&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Parfait</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Le jeu fonctionne parfaitement sans problèmes audio-visuels.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Super</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Le jeu fonctionne avec des problèmes audio-visuels mineurs et est jouable du début jusqu&apos;à la fin. Peut nécessiter des patchs temporaires. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>Correct</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Le jeu fonctionne avec des problèmes audio ou graphiques majeurs, mais est jouable du début à la fin avec des modifications.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Mauvais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Le jeu fonctionne, mais avec des problèmes graphiques ou audio majeurs. Impossible de progresser à certains endroits spécifiques à cause de ces problèmes même en utilisant des modifications. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Le jeu est complètement injouable à cause de problèmes graphique ou audio majeur. Impossible de progresser plus loin que le menu de départ.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Ne se lance pas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Le jeu crash au lancement. .&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sans prendre en compte la vitesse ou les performances, À quel point ce jeu est-il bien émulée du début à la fin sur cette version de yuzu ?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Merci de votre Suggestion !</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Soumission en cours</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Erreur de communication</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Une erreur est survenue en envoyant l&apos;erreur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Suivant</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Moteur de Sortie :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Il s&apos;agit d&apos;un effet de post-traitement qui ajuste la vitesse de l&apos;audio sur celle de l&apos;émulation afin de prévenir les lags du son. Ceci augmente toutefois la latence du son.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Activer l&apos;étirement du son</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Appareil audio :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation>Utiliser le volume global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>Régler le volume:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Volume :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>Général</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation>Précision:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation>Précis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>Peu sûr</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>Activer le mode de débogage</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation>Nous recommandons de régler la précision sur &quot;Précis&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation>Paramètres d&apos;optimisation du CPU non sûrs</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation>Ces réglages réduisent la précision pour la vitesse.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation>Non-utilisation du FMA (améliorer les performances des CPU sans FMA) </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation>
+Cette option améliore la vitesse en réduisant la précision des instructions fusionnées-multiple-ajoutées sur les CPU sans support FMA natif.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation>FRSQRTE et FRECPE plus rapides</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation>
+Cette option améliore la vitesse de certaines fonctions approximatives en virgule flottante en utilisant des approximations natives moins précises.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Les paramètres du CPU ne sont disponibles que lorsque le jeu n&apos;est pas en marche.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation>Mise en mode de débogage du CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation>Le mode de débogage du CPU est uniquement destiné à l&apos;usage des développeurs. Êtes-vous sûr de vouloir l&apos;activer?</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation>Activer les optimisations du CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;
+&lt;b&gt;Pour le débogage uniquement.&lt;/b&gt;
+&lt;br&gt;
+Si vous n&apos;êtes pas sûr de ce qu&apos;elles font, laissez-les toutes activées.
+&lt;br&gt;
+Ces réglages ne prennent effet que lorsque la précision du CPU est en &quot;mode débogage&quot;.
+&lt;/div&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>Activer le &quot;stub&quot; de GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Port :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>S&apos;enregistrer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Filtre de log global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Montrer le journal de logs (Uniquement sur Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Ouvrir l&apos;emplacement du journal de logs</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Chaîne d&apos;arguments</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Activer les services de rapport verbeux</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Ceci sera automatiquement mis à zéro quand yuzu se fermera</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Avancé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Mode Kiosk (Quest)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>Configuration de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Général</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>UI</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Liste des jeux</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>Système</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Profils</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Système de fichier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Contrôles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Raccourcis clavier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Débogage</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Vidéo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Son</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Services</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Répertoire de stockage</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>Carte SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Cartouche de jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Chemin</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Inséré</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Jeu en cours</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Gestionnaire de correctif</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Extraire les fichiers NSOs décompressés</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>Extraire l&apos;ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Racine de chargement de mod</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>Extraire la racine</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Mise en cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Répertoire du cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>Mettre en cache la métadonnée de la liste des jeux</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Mettre à zéro le cache des métadonnées</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Sélectionner le répertoire NAND émulé...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Sélectionner le répertoire SD émulé...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>Sélectionner le chemin de la cartouche de jeu...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Sélectionner le répertoire d&apos;extraction...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Sélectionner le répertoire de chargement de mod...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Sélectionner le répertoire du cache...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>Le cache des métadonnées est déjà vide.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>L&apos;opération s&apos;est terminée avec succès.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>Le cache des métadonnées n&apos;a pas pu être supprimé. Il pourrait être utilisé ou non-existant.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Général </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Limiter la vitesse en pourcentages</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Confirmer la sortie pendent l&apos;émulation en cours</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>Demander l&apos;utilisateur au lancement d&apos;un jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Mettre en pause l’émulation lorsque mis en arrière plan </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Utiliser les shader cache de disque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Utiliser l&apos;émulation GPU asynchrone</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Couleur de L’arrière plan :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Paramètres de raccourcis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>Double-cliquez sur une liaison pour la changer.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Action</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Raccourci clavier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Contexte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Séquence de touches conflictuelle</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Joueur 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Joueur 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Joueur 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Joueur 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Joueur 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Joueur 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Joueur 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Joueur 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Avancé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Configurer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configurer les touches</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Stick Gauche</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Boutons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Stick Droit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Configuration de la souris</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Boutons de la souris</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Avant :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Arrière :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Gauche :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Milieu :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Droite :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Effacer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[pas défini]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Réinitialiser </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[appuyez sur une touche]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Gestionnaire de profil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Utilisateur actuel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Nom d&apos;utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Mettre une image</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Ajouter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Renommer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Supprimer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>La gestion de profil est accessible uniquement lorsque aucun jeu n&apos;est en cours.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Entrez un nom d&apos;utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Utilisateurs</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Entrez un nom d&apos;utilisateur pour le nouvel utilisateur :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Entrez un nouveau nom d&apos;utilisateur :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Confirmez la suppression</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Vous êtes sur le point de supprimer l&apos;utilisateur avec le nom &quot;%1&quot;. Êtes vous sûr?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Sélectionner l&apos;image de l&apos;utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>Images JPEG (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Erreur dans la suppression de l&apos;image</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Une erreur est survenue en essayant de changer l&apos;image précédente à : %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Erreur dans la suppression du fichier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Impossible de supprimer le fichier existant : %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Erreur dans la création du répertoire d&apos;image de l&apos;utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Impossible de créer le répertoire %1 pour stocker les images de l&apos;utilisateur.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Erreur dans la copie de l&apos;image de l&apos;utilisateur</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Impossible de copier l&apos;image de %1 à %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>Le BCAT et le chemin que Nintendo utilise pour envoyer des informations au jeux pour encourager sa communauté à débloquer du contenu additionel.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>BCAT Back-end</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Apprenez en plus sur le BCAT, le Boxcat, et les événements courants&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>Le service boxcat est hors-ligne ou vous n&apos;êtes pas connectés à internet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Une erreur est survenu lors du traitement des données boxcat. Contactez les développeurs de yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>La version de yuzu que vous utilisez est trop vieille ou trop nouvelle pour le serveur. Essayez la dernière mise à jour officiel de yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>Il n&apos;y a pour le moment aucun événements sur boxcat.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>Yuzu récupere le statut de boxcat le plus récent...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Paramètres Système</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Note : ceci peut être remplacé quand le paramètre de région est réglé sur automatique</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Japonais (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Anglais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Français (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Allemand (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Italien (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Espagnol (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Chinois</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Coréen (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Néerlandais (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Portugais (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Russe (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Taïwanais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Anglais Britannique</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Français Canadien</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Espagnol d&apos;Amérique Latine</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Chinois Simplifié</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Chinois Traditionnel (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>RTC Customisé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Langue</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>Seed RNG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Stéréo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Surround</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>ID de la Console :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Mode de sortie Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM yyyy h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Regénérer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>Les paramètres systèmes ne sont accessibles que lorsque le jeu n&apos;est pas en cours.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Ceci remplacera la Switch virtuelle actuelle par une nouvelle. La Switch actuelle ne sera plus récupérable. cela peut entrainer des effets non désirés pendant le jeu. Ceci peut échouer si une configuration de sauvegarde périmée est utilisée. Continuer ?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Avertissement</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>ID de la Console : 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Configurer l&apos;Écran Tactile</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Avertissement : les paramètres de cette page affecte la fonctionnalité intrinsèque de l&apos;écran tactile émulée de yuzu. Toute modification pourrait aboutir à un comportement non désiré, comme par exemple : l&apos;écran tactile qui ne fonctionne plus, de manières partielle ou totale. N&apos;utilisez cette page que si ne vous ne savez ce que vos faites.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Paramètres Tactiles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Diamètres Tactiles Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Doigt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Diamètres Tactiles X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Angle de Rotation</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Restaurer</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>Général</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Note : Changer la langue appliquera votre configuration.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Language de l&apos;interface :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Thème :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Liste de jeux</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Afficher la colonne Des Add-Ons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Grosseur de l&apos;icône</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Texte rangée 1 :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Texte rangée 2 :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Anglais</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forme</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>Service Web yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>En fournissant votre surnom et token, vous acceptez de permettre à yuzu de collecter des données d&apos;utilisation supplémentaires, qui peuvent contenir des informations d&apos;identification de l&apos;utilisateur.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Vérifier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Se connecter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Pseudonyme :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>Qu&apos;est ce que mon token ? </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Télémétrie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Partager les données d&apos;utilisation anonymes avec l&apos;équipe yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>En savoir plus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>ID Télémétrie :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Regénérer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Statut Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Afficher le jeu en cours dans le Statut Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;En savoir plus&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Se connecter&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Quel est mon token ?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>ID Télémétrie : 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Non-spécifié</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>Token non vérifié</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>Le token n&apos;a pas été vérifié. Le changement à votre token n&apos;a pas été enregistré.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Vérification...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Échec de la vérification</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Échec de la vérification. Vérifiez si vous avez correctement entrez votre token, et que votre connection internet fonctionne.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Des données anonymes sont collectées&lt;/a&gt; pour aider à améliorer yuzu. &lt;br/&gt;&lt;br/&gt;Voulez-vous partager vos données d&apos;utilisations avec nous ?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Télémétrie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Échec de la vérification du texte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>Chargement du Web Applet...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Quitter le Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Quitter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Pour quitter l&apos;application web, utilisez les contrôles réserver par le jeu pour sélectionner exit, sélectionner l&apos;option &apos;Quitter le Web Applet&apos; dans la barre menu, ou appuyez sur la touche &apos;Entrer&apos;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Cette version de yuzu à été construit sans le support de QtWebEngine, ce qui veut dire que yuzu ne peut pas correctement afficher le manuel du jeu ou la page web demandée.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Valeur actuelle de la vitesse de l&apos;émulation. Des valeurs plus hautes ou plus basses que 100% indique que l&apos;émulation fonctionne plus vite ou plus lentement qu&apos;une véritable Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Combien d&apos;image par seconde le jeu est en train d&apos;afficher. Ceci vas varier de jeu en jeu et de scènes en scènes.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Temps pris pour émuler une image par seconde de la switch, sans compter le limiteur d&apos;image par seconde ou la synchronisation verticale. Pour une émulation à pleine vitesse, ceci devrait être au maximum à 16.67 ms.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Effacer les Fichiers Récents</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Avertissement : Le Format de jeu est dépassé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Vous utilisez un format de ROM déconstruite pour ce jeu, qui est donc un format dépassé qui à été remplacer par d&apos;autre. Par exemple les formats NCA, NAX, XCI, ou NSP. Les destinations de ROM déconstruites manque des icônes, des métadonnée et du support de mise à jour.&lt;br&gt;&lt;br&gt;Pour une explication des divers formats Switch que yuzu supporte, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;Regardez dans le wiki&lt;/a&gt;. Ce message ne sera pas montré une autre fois.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>Erreur lors du chargement de la ROM !</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>Le format de la ROM n&apos;est pas supporté.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Une erreur s&apos;est produite lors de l&apos;initialisation du noyau dédié à la vidéo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu a rencontré une erreur fatale, veuillez consulter les logs pour plus de détails. Pour plus d&apos;informations sur l&apos;accès aux logs, veuillez consulter la page suivante : &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt; Comment télécharger le fichier des logs &lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Voulez-vous quitter la liste des jeux ? Une émulation continue peut entraîner des crashs, la corruption de données de sauvegarde ou d’autres bugs.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Une erreur inconnue est survenue. Veuillez consulter le journal des logs pour plus de détails.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Démarrer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Enregistrer les données</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Donnés du Mod</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Erreur dans l&apos;ouverture du dossier %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>Le dossier n&apos;existe pas !</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Erreur lors de l&apos;ouverture des Shader Cache Transferable</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>Un shader cache pour ce titre n&apos;existe pas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>L&apos;extraction de la RomFS a échoué !</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Une erreur s&apos;est produite lors de la copie des fichiers RomFS ou l&apos;utilisateur a annulé l&apos;opération.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Plein</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>Squelette</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Sélectionnez le mode d&apos;extraction de la RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Veuillez sélectionner la manière dont vous souhaitez que le fichier RomFS soit extrait.&lt;br&gt;Full copiera tous les fichiers dans le nouveau répertoire, tandis que&lt;br&gt;skeleton créera uniquement la structure de répertoires.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>Extraction de la RomFS ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Annuler</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>Extraction de la RomFS réussi !</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>L&apos;opération s&apos;est déroulée avec succès.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Erreur lors de l&apos;ouverture %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Sélectionner un répertoire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Propriétés</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>Les propriétés du jeu n&apos;ont pas pu être chargées.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Exécutable Switch (%1);;Tous les fichiers (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Charger un fichier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Ouvrir le dossier des ROM extraites</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Destination sélectionnée invalide</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>Le répertoire que vous avez sélectionné ne contient pas de fichier &quot;main&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Installation du fichier &quot;%1&quot; ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Application Système</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Archive Système</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Mise à jour de l&apos;application système</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Paquet micrologiciel (Type A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Paquet micrologiciel (Type B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Mise à jour de jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>DLC de jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Titre Delta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Sélectionner le type d&apos;installation du NCA...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Veuillez sélectionner le type de titre auquel vous voulez installer ce NCA :
+(Dans la plupart des cas, le titre par défaut : &apos;Jeu&apos; est correct.)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Échec de l&apos;installation</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>Le type de titre que vous avez sélectionné pour le NCA n&apos;est pas valide.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Fichier non trouvé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Fichier &quot;%1&quot; non trouvé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Continuer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Erreur d&apos;affichage</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Compte yuzu manquant</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Pour soumettre un test de compatibilité pour un jeu, vous devez lier votre compte yuzu.&lt;br&gt;&lt;br/&gt;Pour lier votre compte yuzu, aller à Emulation &amp;gt; Configuration&amp;gt; Web.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Fichier Amiibo (%1);; Tous les fichiers (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Charger un Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Erreur lors de l&apos;ouverture du fichier de données Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Impossible d&apos;ouvrir le fichier Amiibo &quot;%1&quot; à lire.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Erreur lors de la lecture du fichier de données Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Impossible de lire entièrement les données Amiibo. On s&apos;attend à lire %1 octets, mais il n&apos;a pu lire que %2 octets</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Erreur lors du chargement des données Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Impossible de charger les données Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Capture d&apos;écran</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>Image PNG (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Vitesse : %1% / %2% </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Vitesse : %1% </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Jeu : %1 FPS </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Frame : %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>Le jeu que vous essayez de charger a besoin de fichiers additionnels que vous devez extraire depuis votre Switch avant de jouer.&lt;br/&gt;&lt;br/&gt;Pour plus d&apos;information sur l&apos;extraction de ces fichiers, veuillez consulter la page du wiki suivante : &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Extraction des archives système et des Shared Fonts depuis la Switch&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Voulez-vous quitter la liste des jeux ? Une émulation continue peut entraîner des crashs, la corruption de données de sauvegarde ou d’autres bugs.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>yuzu n&apos;a pas été capable de localiser un système d&apos;archive Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>yuzu n&apos;a pas été capable de localiser un système d&apos;archive Switch. %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Archive système introuvable</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Archive Système Manquante</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>Yuzu n&apos;a pas été capable de localiser les polices partagées de la Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Les polices partagées non pas été trouvées</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Polices Partagée Manquante</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Erreur fatale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu a rencontré une erreur fatale, veuillez consulter les logs pour plus de détails. Pour plus d&apos;informations sur l&apos;accès aux logs, veuillez consulter la page suivante : &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt; Comment télécharger le fichier des logs &lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Voulez-vous quitter la liste des jeux ? Une émulation continue peut entraîner des crashs, la corruption de données de sauvegarde ou d’autres bugs.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Erreur Fatale rencontrée</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Confirmer la réinstallation de la clé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Vous êtes sur le point de réinstaller toutes vos clés.
+Si vous ne savez pas ce que cela veut dire et ce que vous faites,
+cela peut être une action potentiellement destructrice.
+S&apos;il vous plaît assurez-vous que c&apos;est bien ce que vous voulez
+et éventuellement faites des sauvegardes.
+
+Cela supprimera vos fichiers de clé générés automatiquement et ré exécutera le module d&apos;installation de clé.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Installation des clés...
+Cela peut prendre jusqu&apos;à une minute en fonction
+des performances de votre système.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Installation des clés</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Sélectionner la cible d&apos;extraction du RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Veuillez sélectionner quel RomFS vous voulez extraire.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Êtes vous sûr de vouloir fermer yuzu ?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Êtes-vous sûr d&apos;arrêter l&apos;émulation ? Tout progrès non enregistré sera perdu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>L&apos;application en cours a demandé à yuzu de ne pas quitter.
+
+Voulez-vous ignorer ceci and quitter quand même ?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Nom</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Compatibilité</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Extensions</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Type de fichier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Taille</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Ouvrir l&apos;emplacement des données de sauvegarde</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Ouvrir l&apos;emplacement des données des mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Ouvrir le cache de shaders transférable</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Extraire la RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Copier l&apos;ID du titre dans le Presse-papiers</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Accédez à l&apos;entrée GameDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Propriétés</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Scanner les sous-dossiers</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Supprimer le répertoire du jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Ouvrir l&apos;emplacement du répertoire</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Parfait</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>Le jeu fonctionne parfaitement, de manière fluide sans aucun bug audio ou graphique, toutes les fonctionnalités testées fonctionnent comme prévu sans
+aucune modification nécessaire.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Bon</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>Le jeu fonctionne correctement avec des bugs audio ou graphiques mineurs et est jouable du début à la fin. Nécessite peut être des
+modifications</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Ok</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>Le jeu fonctionne avec des bugs audio ou graphiques majeurs, mais il est jouable du début à la fin avec des modifications.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Mauvais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>Le jeu fonctionne mais avec des bugs audio et graphiques majeurs. Impossible de progresser dans certaines zones à causes des bugs
+même avec des modifications.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>Le jeu est complètement injouable à cause de bugs audio et graphiques. Impossible de progresser plus loin que l&apos;écran de démarrage.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Ne démarre pas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>Le jeu crash au lancement.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Non testé</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>Le jeu n&apos;a pas encore été testé.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Double-cliquez pour ajouter un nouveau dossier à la liste de jeux</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filtre :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Entrez un motif à filtrer</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>Chargement des shaders 387 / 1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>Chargement des shaders %v sur %m</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Temps Estimé 5m 4s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>Chargement...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>Chargement des shaders %1 / %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Lancement...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Temps Estimé %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Fichier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Fichiers récents</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Émulation</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Vue</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Débogage</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Outils</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Aide</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Charger un fichier ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Charger un dossier ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>Q&amp;uitter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Démarrer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pause</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Arrêter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Réinitialiser les clés ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>À propos de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Mode fenêtre unique</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Configurer ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Afficher les &quot;Dock Widget Headers&quot;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Afficher la barre de filtre</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Afficher la barre d&apos;état</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Plein écran</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Redémarrer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Charger un Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Signaler la compatibilité</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Ouvrir le dossier de yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Prendre une capture d&apos;ecran</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroProfil</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Titres installés sur la SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Titres installés sur la NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Titres Système</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Ajouter un nouveau répertoire de jeu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Maj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[non défini]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Chapeau %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Axe %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Bouton %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[inconnu]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[inutilisé]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Axe %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>Une erreur s&apos;est produite.
+Veuillez essayer à nouveau ou contactez le développeur du logiciel.
+
+Code d&apos;Erreur: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>Une erreur s&apos;est produite le %1 à %2.
+Veuillez essayer à nouveau ou contactez le développeur du logiciel.
+
+Code d&apos;Erreur: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>Une erreur s&apos;est produite.
+Code d&apos;Erreur: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Choisir un utilisateur :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Utilisateurs</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Sélecteur de profil</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Entrer le texte :</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Clavier virtuel</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Entrez une hotkey</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Pile d&apos;exécution</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>en attente du &quot;mutex&quot; 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>En attente : %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>propriétaire de la manche : 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>en attente de tous les objets</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>en attente d&apos;un des objets suivants</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>en cours</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>prêt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>en pause</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>en attente du retour de HLE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>en veille</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>en attente de réponse IPC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>En attente d&apos;objets</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>En attente du &quot;mutex&quot;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>en attente de la variable conditionnelle</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>En attente de l&apos;adresse arbitre</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>dormant</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>Mort</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation>PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>idéal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>cœur %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Processeur inconnu %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>Processeur = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>Cœur idéal = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>masque d&apos;affinité = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>id du fil = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>priorité = %1(courant) / %2(normal)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>dernier tick en cours = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>en attente du &quot;mutex&quot;</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>attendu par un fil</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Arbre d&apos;attente</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/it.ts b/dist/languages/it.ts
new file mode 100644
index 000000000..09c832fd3
--- /dev/null
+++ b/dist/languages/it.ts
@@ -0,0 +1,4724 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="it" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>Riguardo yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu è un emulatore open source sperimentale per Nintendo Switch sotto licenza GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;Questo software non dovrebbe essere utilizzato per giocare a giochi che non hai ottenuto legalmente&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Sito&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Codice Sorgente&lt;/span&gt;&lt;/a&gt; I &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributori&lt;/span&gt;&lt;/a&gt; I &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Licenza&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; è un marchio registrato di Nintendo. yuzu non è affiliato a Nintendo in alcun modo.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>In comunicazione con il server...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>Annulla</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>Configurazione completata!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Segnala Compatibilità</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Segnala la Compatibilità di un Gioco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Se dovessi scegliere di inviare un rapporto alla &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Lista Compatibilità di yuzu&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, Le seguenti informazioni saranno raccolte e visualizzate sul sito: &lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Informazioni sull&apos;Hardware (CPU / GPU / Sistema Operativo)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Quale versione di yuzu stai utilizzando&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;L&apos;account di yuzu connesso&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Perfetto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Il gioco funziona impeccabilmente senza errori di audio o grafici.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Buono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Il gioco funziona con qualche errore grafico minore o glitch nell&apos;audio ed è giocabile dall&apos;inizio alla fine. Potrebbe richiedere qualche metodo alternativo.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>Ok</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Il gioco funziona e ha errori grafici gravi o glitch nell&apos;audio ma è possibile giocare dall&apos;inizio alla fine con dei metodi alternativi.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Scadente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Il gioco presenta alcuni errori audio o video. E&apos; impossibile proseguire in alcune aree a causa della presenza di glitch persino utilizzando metodi alternativi.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Questo gioco è completamente ingiocabile a causa di gravi errori audio o video. E&apos; impossibile proseguire oltre la schermata iniziale.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Non si Avvia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Il gioco si blocca quando viene avviato.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Indipendentemente da velocità o prestazioni, come ti è sembrato giocare questo gioco dall&apos;inizio alla fine su questa versione di yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Grazie per la tua segnalazione!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Inviando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Errore di comunicazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>E&apos; stato riscontrato un errore mandando il Testcase</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Prossimo</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Motore dell&apos;Output:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>L&apos;effetto post-processing regola la velocità dell&apos;audio per fare in modo che sia uguale alla velocità del gioco e prevenire problemi di audio. Questo tuttavia aumenta la latenza dell&apos;audio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Abilita allungamento dell&apos;audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Dispositivo Audio:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation>Usa volume globale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>Imposta volume:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Volume:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>Generale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>Non sicuro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>Abilita Modalità Debug</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation>Impostazioni Ottimizzazione CPU non sicura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>Abilita GDB Stub</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Porta:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Logging</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Filtro Log Globale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Mostra Console Log (Solo per Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Apri Cartella Log</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Stringa degli Argomenti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation>Grafica</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Abilita Servizi di Segnalazione Dettagliata</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Verrà ripristinato automaticamente dopo la chiusura di yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Avanzate</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Modalità Kiosk (Quest)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>Configurazione di yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Generale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>Interfaccia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Lista Giochi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Profili</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Filesystem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Comandi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Hotkey</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Debug</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Grafica</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Servizi</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Cartelle di Archiviazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>Scheda SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Cartuccia di gioco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Percorso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Inserita</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Gioco In Uso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Gestione Patch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Estrai NSO Decompressi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>Estrai ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Cartella Caricamento Mod</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>Cartella di Estrazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Caching</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Cartella Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>Crea Cache di Metadati Lista Giochi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Elimina Cache dei Metadati</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Seleziona Cartella NAND Emulata</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Seleziona Cartella Scheda SD Emulata</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>Seleziona Percorso Cartuccia di Gioco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Seleziona Cartella di Estrazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Seleziona Cartella per il Caricamento delle Mod</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Seleziona Cartella della Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>La cache dei metadati è già vuota.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>L&apos;operazione è stata completata con successo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>Impossibile eliminare cache dei metadati. Potrebbe essere in uso o inesistente.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Generale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Percentuale Limite Velocità</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Richiedi conferma di uscire mentre l&apos;emulazione è in corso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>Richiedi utente all&apos;avvio di un gioco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Pausa emulazione quando in background</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation>Impostazioni API</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation>API:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation>Dispositivo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation>Impostazioni grafiche</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Usa cache shader su disco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Usa emulazione GPU asincrona</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation>Default (16:9)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation>Usa il colore di sfondo globale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation>Imposta colore dello sfondo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Colore Sfondo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation>2x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation>4x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation>8x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation>16x</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Impostazioni Hotkey</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>Clicca due volte su un collegamento per cambiarlo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Azione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Hotkey</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Contesto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Conflitto nella Sequenza di Tasti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Giocatore 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Giocatore 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Giocatore 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Giocatore 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Giocatore 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Giocatore 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Giocatore 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Giocatore 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Avanzate</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation>Vibrazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Configura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation>2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation>3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation>4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation>5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation>6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation>7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation>8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation>Connesso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation>Predefiniti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation>Giocatore 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation>Giocatore 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation>Giocatore 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation>Giocatore 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation>Giocatore 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation>Giocatore 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation>Giocatore 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation>Giocatore 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation>Altro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation>Tastiera</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation>Avanzate</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation>Touchscreen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation>Mouse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation>Configura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation>Debug Controller</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configura Input</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation>Salva</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation>Nuovo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation>Elimina</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Stick Sinistro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation>Su</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation>Sinistra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation>Destra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation>Giù</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation>Premuto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation>Modificatore</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation>ZL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation>Meno</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation>Cattura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation>Più</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation>Home</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation>ZR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation>SL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation>SR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Pulsanti Frontali</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Stick Destro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation>[in attesa]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation>Sensibilità:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation>Touch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation>Calibrazione:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation>(100, 50) - (1800, 850)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation>Configura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Configura Mouse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Pulsanti Mouse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Avanti:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Indietro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Sinistro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Centrale:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Destro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Cancella</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[non impostato]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Ripristina Predefinito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[premi un pulsante]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Gestione Profili</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Utente in Uso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Nome Utente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Imposta Immagine</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Aggiungi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Rinomina</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Rimuovi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>La gestione dei profili è disponibile solamente quando nessun gioco è in esecuzione.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>&amp;1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Inserisci Nome Utente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Utenti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Inserisci un nome utente per il nuovo utente:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Inserisci un nuovo nome utente:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Conferma Eliminazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Stai per cancellare l&apos;utente chiamato &quot;%1&quot;. Sei sicuro?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Seleziona Immagine Utente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>Immagini JPEG (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Impossibile eliminare l&apos;immagine</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Impossibile sovrascrivere l&apos;immagine precedente in: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Impossibile eliminare il file</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Impossibile eliminare il file già esistente: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Errore nella creazione della cartella delle immagini dell&apos;utente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Impossibile creare la cartella %1 per archiviare le immagini dell&apos;utente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Impossibile copiare l&apos;immagine utente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Impossibile copiare l&apos;immagine da %1 a %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>Nintendo usa BCAT per inviare dati ai giochi per coinvolgere la comunità e sbloccare contenuti aggiuntivi.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>Backend BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Scopri di più riguardo BCAT, Boxcat, ed Eventi in Corso&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>Il servizio boxcat è offline o potresti non essere connesso a internet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>E&apos; stato riscontrato un errore nell&apos;elaborazione dei dati degli eventi di boxcat. Contatta gli sviluppatori di yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>La versione di yuzu che stai usando è troppo vecchia o troppo nuova per questo server. Prova ad aggiornare all&apos;ultima release ufficiale di yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>Non ci sono eventi in corso su boxcat.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>yuzu sta recuperando lo stato attuale di boxcat...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Impostazioni di Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation>UTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation>W-SU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation>WET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation>Zulu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation>USA</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation>Europa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation>Australia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation>Cina</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation>Corea</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation>Taiwan</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Nota: questo può essere ignorato quando le impostazioni della regione sono su auto-select</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Giapponese (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Inglese (English)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Francese (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Tedesco (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Italiano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Spagnolo (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Cinese</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Coreano (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Olandese (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Portoghese (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Russo (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Taiwanese</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Inglese Britannico</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Francese Canadese</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Spagnolo Latino Americano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Cinese Semplificato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Cinese Tradizionale (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>RTC Personalizzato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Lingua</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>Seed RNG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Stereo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Surround</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>ID Console:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Modalità di output del sonoro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM yyyy h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Rigenera</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>Le impostazioni di sistema sono disponibili solamente quando nessun gioco è in esecuzione.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Questo rimpiazzerà la tua Switch virtuale con una nuova. La tua Switch virtuale non sarà recuperabile. Questo potrebbe avere effetti indesiderati nei giochi. Questo potrebbe fallire, se usi un salvataggio non aggiornato. Desideri continuare?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Attenzione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>ID Console: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation>Nuovo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation>Elimina</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation>Rinomina</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation>Elimina Punto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation>Pulsante</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation>Nuovo Profilo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation>Inserisci il nome per il nuovo profilo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation>Elimina Profilo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation>Elimina profilo %1?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation>Rinomina Profilo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation>Nuovo nome:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation>[premi pulsante]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Configura Touchscreen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Attenzione: Le impostazioni in questa pagina influenzano il funzionamento interno del touchscreen emulato di yuzu. Cambiarle potrebbe risultare in effetti indesiderati, ad esempio il touchscreen potrebbe non funzionare parzialmente o totalmente. Utilizza questa pagina solo se sei consapevole di ciò che stai facendo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Parametri Touch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Diametro Touch Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Dito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Diametro Touch X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Angolo di Rotazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Ripristina Predefiniti</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>Generale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Nota: Cambiare lingua applicherà i cambiamenti fatti alla configurazione.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Lingua Interfaccia:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Tema:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Lista Giochi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Mostra Colonna Add-On</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Dimensione Icona:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Testo Riga 1:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Testo Riga 2:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation>Screenshots</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Inglese</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>Servizio Web di yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Fornendo i tuoi nome utente e token, permetti a yuzu di raccogliere dati di utilizzo, che potrebbero includere informazioni di identificazione utente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Verifica</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Registrati</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Nome utente:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>Qual è il mio token?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Condividi dati sull&apos;utilizzo anonimamente con il team di yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Per saperne di più</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>ID Telemetria:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Rigenera</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Discord Presence</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Mostra il Gioco Attuale nel tuo Stato di Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Per saperne di più&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Registrati&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Qual è il mio token?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>ID Telemetria: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Non specificato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>Token non verificato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>Nome utente e token non verificati. I cambiamenti al tuo nome utente e/o token non sono stati salvati.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Verifica in corso...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Verifica fallita</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Verifica fallita. Controlla di aver inserito correttamente il tuo username e token, e che la tua connessione ad internet sia funzionante.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Vengono raccolti dati anonimi&lt;/a&gt; per aiutarci a migliorare yuzu. &lt;br/&gt;&lt;br/&gt;Desideri condividere i tuoi dati di utilizzo con noi?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Verificazione Testo Fallita</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>Caricamento Web Applet...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Esci dal Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Esci</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Per uscire dall&apos;applicazione web, usa i pulsanti provvisti dal gioco per uscire, seleziona l&apos;opzione &apos;Esci dal Web Applet&apos; nel menu in alto, o premi il tasto &apos;Invio&apos;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Questa versione di yuzu è stata compilata senza supporto a QtWebEngine, quindi yuzu non potrà aprire il manuale di gioco o delle pagine web.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Velocità corrente dell&apos;emulazione. Valori più alti o più bassi di 100% indicano che l&apos;emulazione sta funzionando più velocemente o lentamente rispetto a una Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Quanti frame al secondo il gioco mostra attualmente. Questo varia da gioco a gioco e da situazione a situazione.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Tempo utilizzato per emulare un frame della Switch, non contando i limiti ai frame o il v-sync.
+Per un&apos;emulazione alla massima velocità, il valore dev&apos;essere al massimo 16.67 ms.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Elimina File Recenti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Avviso Formato di Gioco Obsoleto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Stai usando una cartella con dentro una ROM decostruita come formato per avviare questo gioco, è un formato obsoleto ed è stato sostituito da altri come NCA, NAX, XCI o NSP. Le ROM decostruite non hanno icone, metadata e non supportano gli aggiornamenti. &lt;br&gt;&lt;br&gt;Per una spiegazione sui vari formati di Switch che yuzu supporta, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;controlla la nostra wiki&lt;/a&gt;. Questo messaggio non verrà più mostrato.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>Errore nel caricamento della ROM!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>Il formato della ROM non è supportato.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>E&apos; stato riscontrato un errore nell&apos;inizializzazione del core video.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu ha riscontrato un errore nell&apos;esecuzione del video core, visualizza il log per maggiori dettagli. Per maggiori informazioni su come accedere al log, visualizza la seguente pagina: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Come Caricare Il File Log&lt;/a&gt;. Assicurati di avere gli ultimi driver della tua GPU.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>E&apos; stato riscontrato un errore sconosciuto. Visualizza il log per maggiori dettagli.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Avvia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Dati di Salvataggio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Dati delle Mod</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Errore nell&apos;Apertura della Cartella %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>La cartella non esiste!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Errore nell&apos;Apertura della Cache Shader Trasferibile</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>Una cache di shader per questo titolo non esiste.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation>Contenuti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation>Aggiorna</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation>DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>Estrazione RomFS Fallita!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>C&apos;è stato un errore nella copia dei file del RomFS o l&apos;operazione è stata annullata dall&apos;utente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Completa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>Scheletro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Seleziona Modalità Estrazione RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Seleziona come vorresti estrarre il RomFS. &lt;br&gt;Completo copierà tutti i file in una nuova cartella mentre&lt;br&gt;scheletro creerà solamente le cartelle e le sottocartelle.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>Estrazione RomFS in corso...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Annulla</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>Estrazione RomFS Riuscita!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>L&apos;operazione è stata completata con successo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Errore nell&apos;Apertura di %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Seleziona Cartella</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Proprietà</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>Le proprietà del gioco non sono potute essere caricate.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Eseguibile Switch (%1);;Tutti i File (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Carica File</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Apri Cartella ROM Estratta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Cartella Selezionata Non Valida</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>La cartella che hai selezionato non contiene un file &quot;main&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Installazione del file &quot;%1&quot;...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Applicazione di Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Archivio di Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Aggiornamento Applicazione di Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Pacchetto Firmware (Tipo A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Pacchetto Firmware (Tipo B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Gioco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Aggiornamento di Gioco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>DLC Gioco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Titolo Delta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Seleziona il Tipo di Installazione NCA</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Seleziona il tipo del file NCA da installare:
+(Nella maggior parte dei casi, il predefinito &apos;Gioco&apos; va bene.)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Installazione Fallita</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>Il tipo che hai selezionato per l&apos;NCA non è valido.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>File non trovato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>File &quot;%1&quot; non trovato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Continua</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Messaggio di Errore</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Account di yuzu non trovato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Per segnalare la compatibilità di un gioco, devi collegare il tuo account yuzu. &lt;br&gt;&lt;br/&gt;Per collegare il tuo account yuzu, vai su Emulazione &amp;gt;
+Configurazione &amp;gt; Web.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>File Amiibo (%1);; Tutti I File (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Carica Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Errore nell&apos;apertura del file dati Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Impossibile aprire e leggere il file Amiibo &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Errore nella lettura dei dati del file Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Impossibile leggere tutti i dati dell&apos;Amiibo. E&apos; stato possibile leggere solamente %2 byte di %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Errore nel caricamento dei dati dell&apos;Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Impossibile caricare i dati dell&apos;Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Cattura Screenshot</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>Immagine PNG (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Velocità: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Velocità: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Gioco: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Frame: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>Il gioco che stai provando a caricare richiede ulteriori file che devono essere estratti dalla tua Switch prima di poter giocare. &lt;br/&gt;&lt;br/&gt;Per maggiori informazioni sull&apos;estrazione di questi file, visualizza la seguente pagina della wiki: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Estrazione di Archivi di Sistema e Font Condivisi da una Console Switch&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Vuoi uscire e tornare alla lista dei giochi? Continuare l&apos;emulazione potrebbe risultare in crash, salvataggi corrotti o altri bug.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>yuzu non ha potuto individuare un archivio di sistema della Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>yuzu non ha potuto individuare un archivio di sistema della Switch: %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Archivio di Sistema Non Trovato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Archivio di Sistema Mancante</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>yuzu non ha potuto individuare i font condivisi della Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Font Condivisi Non Trovati</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Font Condivisi Mancanti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Errore Fatale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu ha riscontrato un errore fatale, visualizza il log per maggiori dettagli. Per maggiori informazioni su come accedere al log, visualizza la seguente pagina: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Come Caricare Il File Log&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Vuoi uscire e tornare alla lista dei giochi? Continuare l&apos;emulazione potrebbe risultare in crash, salvataggi corrotti o altri bug.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Errore Fatale riscontrato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Conferma Riderivazione Chiave</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Stai per forzare la ri-derivazione di tutte le tue chiavi.
+Se non sai cosa significa o cosa stai per fare,
+questa azione potrebbe causare gravi problemi.
+Assicurati di volerlo fare
+e facoltativamente fai dei backup.
+
+Questo eliminerà i tuoi file di chiavi autogenerati e ri-avvierà il processo di derivazione delle chiavi.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Derivazione chiavi...
+Questa operazione potrebbe durare fino a un minuto in
+base alle prestazioni del tuo sistema.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Derivazione Chiavi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Seleziona Target dell&apos;Estrazione del RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Seleziona quale RomFS vorresti estrarre.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Sei sicuro di voler chiudere yuzu?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Sei sicuro di voler fermare l&apos;emulazione? Tutti i progressi non salvati verranno perduti.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>L&apos;applicazione in uso ha richiesto a yuzu di non uscire.
+
+Desideri uscire comunque?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Nome</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Compatibilità</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Add-on</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Tipo di file</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Dimensione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Apri Cartella Dati di Salvataggio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Apri Cartella Mod</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Apri Cache Shader Trasferibile</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Estrai RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Copia il Title ID negli Appunti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Vai alla pagina di GameDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Proprietà</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Scansiona Sottocartelle</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Rimuovi Cartella Giochi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Apri Cartella</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Perfetto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>Il gioco funziona perfettamente senza alcuni glitch audio o video, tutte le funzionalità testate funzionano come dovrebbero senza alcun metodo alternativo necessario.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Buono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>Il gioco presenta qualche errore grafico minore o glitch nell&apos;audio ed è giocabile dall&apos;inizio alla fine. Potrebbe richiedere dei metodi alternativi.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Ok</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>l gioco presenta alcuni errori gravi audio o video. E&apos; impossibile proseguire in alcune aree a causa della presenza di glitch persino utilizzando metodi alternativi. </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Scadente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>Il gioco funziona, ma presenta alcuni errori gravi audio o video. È impossibile proseguire in alcune aree a causa della presenza di glitch
+persino utilizzando metodi alternativi.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>Il gioco è completamente ingiocabile a causa di errori gravi audio o video. È impossibile proseguire oltre la Schermata
+Iniziale.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Non si Avvia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>Il gioco si blocca quando viene avviato.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Non Testato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>Il gioco non è ancora stato testato.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Clicca due volte per aggiungere una nuova cartella alla lista dei giochi</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filtro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Inserisci pattern per filtrare</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>387 / 1628 Shader Caricate</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>Caricamento di %v su %m Shader</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Tempo Stimato 5m 4s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>Caricamento...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>%1 / %2 Shader Caricate</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Avvio in corso...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Tempo Stimato %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;File</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>File Recenti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Emulazione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Visualizza</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Debugging</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Strumenti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Aiuto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Carica File...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Carica Cartella...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>&amp;Esci</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Avvia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pausa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Ri-inizializzando le chiavi...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>Riguardo yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Modalità Finestra Singola</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Configura...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Visualizza le Intestazioni del Dock dei Widget</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Mostra Barra Filtri</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Mostra Barra Stato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Schermo Intero</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Riavvia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Carica Amiibo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Segnala Compatibilità</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Apri Cartella yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Cattura Screenshot</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroProfile</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Titoli SD Installati</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Titoli NAND Installati</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Titoli di Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Aggiungi Nuova Cartella Giochi</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[non impostato]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Hat %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Asse %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Pulsante %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[sconosciuto]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation>Clicca 0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation>Clicca 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation>Clicca 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation>Clicca 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation>Clicca 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation>Assi GC %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation>Pulsanti GC %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[inutilizzato]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Asse %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation>Assi GC %1</translation>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>E&apos; stato riscontrato un errore.
+Riprova o contatta gli sviluppatori del software.
+
+Codice Errore: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>E&apos; stato riscontrato un errore su %1 a %2.
+Riprova o contatta gli sviluppatori del software.
+
+Codice Errore: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>E&apos; stato riscontrato un errore.
+Codice Errore: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Seleziona un utente:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Utenti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Selettore Profili</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Inserisci testo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Tastiera Software</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Inserisci una hotkey</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Stack chiamata</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>attende il mutex 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>ha dei waiter: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>proprietario handle: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>attende tutti gli oggetti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>attende uno dei seguenti oggetti</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation>[%1]%2 %3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation>atteso da nessun thread</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>in funzione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>pronto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>in pausa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>attende ritorno dell&apos;HLE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>dormendo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>attende una risposta dell&apos;IPC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>attende degli oggetti</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>attende un mutex</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>aspettando la condition variable</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>attende un indirizzo arbitro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>dormiente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>morto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation> PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>ideale</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>core %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Processore sconosciuto %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>processore = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>core ideale = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>affinity mask = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>id thread = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>priorità = %1(corrente) / %2(normale)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>last running ticks = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>non attende un mutex</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>atteso dal thread</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Wait Tree</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/ja_JP.ts b/dist/languages/ja_JP.ts
new file mode 100644
index 000000000..4c3870603
--- /dev/null
+++ b/dist/languages/ja_JP.ts
@@ -0,0 +1,4752 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="ja_JP" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>yuzuについて</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzuはGPLv2.0の下で提供されている任天堂Switchの実験的なオープンソースエミュレータです。&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;このソフトウェアは違法に入手したゲームを遊ぶために使用されるべきではありません。&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;ウェブサイト&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;ソースコード&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;貢献者&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;ライセンス&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;任天堂 Switch&amp;quot;は任天堂の登録商標です。 yuzuは任天堂と提携しているわけではありません。&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>サーバと通信中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>キャンセル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation>タッチパッドの左上を&lt;br&gt;タッチして下さい。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation>次にタッチパッドの右下を&lt;br&gt;タッチして下さい。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>設定が完了しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>互換性を報告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>ゲームの互換性を報告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;テストケースを&lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu互換性リスト&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;に送信した場合、以下の情報が収集され、サイトに表示されます:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;ハードウェア情報(CPU/GPU/OS)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;実行中のyuzuバージョン&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;接続中のyuzuアカウント&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>パーフェクト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ゲームはオーディオやグラフィックの不具合なしに完全に動作します。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>グレート</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ゲームの動作にはグラフィック、またはオーディオの軽微な不具合がありますが、最初から最後までプレイ可能です。いくつかの回避策が必要な場合があります。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>OK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ゲームの動作にはグラフィック、またはオーディオの重大な不具合がありますが、回避策を使うことで最初から最後までプレイ可能です。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>NG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ゲームは動作しますが、グラフィック、またはオーディオに重大な不具合があります。回避策を使用しても不具合が原因で特定の場所から進めなくなります。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>イントロ/メニューまで</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;グラフィック、またはオーディオの重大な不具合のため、ゲームはプレイ不可能です。スタート画面から先に進めることは出来ません。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>起動せず</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;ゲームは起動時にクラッシュします。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;速度やパフォーマンスに関係なく、このゲームはこのバージョンのyuzuで最初から最後までどの程度うまく実行できていますか?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>ご協力ありがとうございます!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>送信中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>通信エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>テストケースを送信中にエラーが発生しました</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>次</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>オーディオ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>出力エンジン:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>後処理エフェクトは、エミュレーション速度と一致するようにオーディオ速度を調整し、オーディオの乱れを防ぐのに役立ちます。ただしオーディオの遅延が長くなります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>オーディオストレッチの有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>オーディオデバイス:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation>共通の音量設定を使用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>音量設定:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>音量:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>全般</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation>正確度:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation>正確</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>不安定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>デバッグモードを使用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation>正確度は”正確”に設定することを推奨します。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation>CPU最適化設定(不安定)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation>これらの設定は高速化のために正確性を犠牲にします。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation>FMAの統合を解除(FMAを持たないCPUにおいて速度を改善する)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;このオプションは、ネイティブFMAのサポートを持たないCPUで融合積和命令の精度を下げることで速度を改善させます。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation>高速FRSQRTEとFRECPE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;このオプションは、精度の低いネイティブ近似を使用することにより、一部の近似浮動小数点関数の速度を改善させます。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>CPU設定は、ゲームが実行されていない場合にのみ使用できます。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation>CPUをデバッグモードに設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation>CPUデバッグモードは、開発者による使用のみを目的としています。有効にしてもよろしいですか?</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation>CPUの最適化を切り替え</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;
+ &lt;b&gt;デバッグ用&lt;/b&gt;
+ &lt;br&gt;
+ これらの機能が何を意味するのか分からない場合は、すべて有効にしておいてください。
+ &lt;br&gt;
+ これらの設定は、CPU精度が”デバッグモード”の場合にのみ有効になります。
+ &lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation>インラインページテーブルの有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;この最適化により、ゲストプログラムによるメモリアクセスが高速化されます。&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;これを有効にすると、PageTable :: pointersへのアクセスが発行されたコードにインライン化されます。&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;これを無効にすると、すべてのメモリアクセスが強制的にMemory :: Read / Memory :: Write関数を経由します。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation>ブロックリンクの有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;この最適化では、宛先PCが静的な場合、発行された基本ブロックが他の基本ブロックに直接ジャンプできるようにすることで、ディスパッチャーのルックアップを回避します。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation>リターンスタックバッファの有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;この最適化は、BL命令の潜在的な戻りアドレスを追跡することにより、ディスパッチャーの検索を回避します。これは、実際のCPUのリターンスタックバッファで何が起こるかを概算します。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation>高速ディスパッチャの有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;2層のディスパッチシステムを有効にします。アセンブリで記述されたより高速なディスパッチャには、ジャンプ先の小さなMRUキャッシュが最初に使用されます。それが失敗した場合、ディスパッチはより遅いC ++ディスパッチャーにフォールバックします。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation>コンテキスト消去の有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;CPUコンテキスト構造への不要なアクセスを削減するIR最適化を有効にします。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation>定数伝播の有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;一定の伝播を伴うIR最適化を有効にします。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation>その他の最適化を有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;その他のIR最適化を有効にします。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation>ミスアライメントチェックの削減を有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;有効にすると、アクセスがページの境界を越えたときにのみ、ミスアライメントがトリガーされます。&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;無効にすると、ミスアライメントされたすべてのアクセスでミスアライメントがトリガーされます。&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>CPU設定は、ゲームが実行されていない場合にのみ使用できます。</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>GDBスタブの有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>ポート:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>ログの記録</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>グローバルログフィルター</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>ログコンソールの表示(Windowsのみ)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>ログ出力フォルダを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>自作</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>引数文字列</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation>グラフィック</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation>オンにすると、グラフィックAPIはより遅いデバッグモードになります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation>グラフィックデバッグの有効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation>オンにすると、マクロJust In Timeコンパイラが無効になります。有効にすると、ゲームの実行が遅くなります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation>マクロJITの無効化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation>ダンプ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>詳細なレポートサービスを有効にする</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>yuzuを終了したときに自動的にリセットされます。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>拡張</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>キオスク (クエスト) モード</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation>デバッグコントローラ設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation>消去</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation>デフォルト</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>yuzuの設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>全般</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>UI</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>ゲームリスト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>システム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>プロファイル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>ファイルシステム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>コントロール</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>ホットキー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation>CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>デバッグ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>グラフィック</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation>拡張</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation>拡張グラフィック</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>オーディオ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>サービス</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>ストレージディレクトリ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>SDカード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>ゲームカード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>パス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>挿入する</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>現在のゲーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>パッチマネージャ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>解凍されたNSOをダンプ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>ExeFSをダンプ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Modロードディレクトリ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>ダンプディレクトリ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>キャッシュ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>キャッシュディレクトリ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>ゲームリストのメタデータをキャッシュする</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>メタデータのキャッシュをクリアする</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>NANDディレクトリを選択...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>SDディレクトリを選択...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>ゲームカードのパスを選択...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>ダンプディレクトリを選択...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Modロードディレクトリを選択...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>キャッシュディレクトリを選択...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>メタデータのキャッシュはすでに空です。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>操作は成功しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>メタデータのキャッシュを削除できませんでした。使用中、または存在していない可能性があります。</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>全般</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>速度パーセントの制限</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation>マルチコアCPUエミュレーション</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>エミュレーション実行中の終了確認</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>ゲーム起動時に確認を表示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>バックグラウンド時にエミュレーションを停止</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation>未使用時にマウスを非表示</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation>API設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation>API:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation>デバイス:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation>グラフィック設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>ディスクシェーダキャッシュを使用する</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>非同期GPUエミュレーションを使用する</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation>アスペクト比:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation>デフォルト (16:9)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation>4:3を強制</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation>21:9を強制</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation>ウィンドウに合わせる</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation>共通の背景色を使用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation>背景色の設定:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>背景色:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation>OpenGLグラフィックデバイス</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation>拡張グラフィック設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation>正確性レベル:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation>VSyncは画面のティアリングを防ぎますが、一部のグラフィックカードはVSyncが有効になっているとパフォーマンスが低下します。パフォーマンスの違いに気づかない場合は、有効にしてください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation>VSyncを使用(OpenGLのみ)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation>これを有効にすると、シェーダースタッターが減少します。サポートされているNvidiaデバイスでOpenGLアセンブリシェーダーを有効にします(NV_gpu_program5が必要です)。この機能は実験的なものです。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation>アセンブリシェーダーを使用します(実験的、Nvidia OpenGLのみ)。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation>非同期シェーダーコンパイルを有効にします。これにより、シェーダースタッターが減少する場合があります。この機能は実験的なものです。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation>非同期シェーダー構築を使用します(実験的)。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation>高速CPU時間を使用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation>異方性フィルタリング:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation>デフォルト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation>2x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation>4x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation>8x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation>16x</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>ホットキー設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>変更するには項目をダブルクリックしてください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation>すべて消去</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation>デフォルトに戻す</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>アクション</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>ホットキー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>コンテキスト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>キー設定の衝突</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation>入力されたキーシーケンスは既に割り当てられています:%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation>デフォルトに戻す</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation>消去</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation>デフォルトのキーシーケンスは既に割り当てられています:%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation>入力設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>プレイヤー1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>プレイヤー2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>プレイヤー3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>プレイヤー4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>プレイヤー5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>プレイヤー6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>プレイヤー7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>プレイヤー8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>拡張</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation>コンソールモード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation>接続</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation>接続解除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation>バイブレーション</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation>モーション</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation>コントローラ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation>2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation>3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation>4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation>5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation>6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation>7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation>8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation>接続</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation>デフォルト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation>消去</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>入力設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation>Joyconカラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation>プレイヤー1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation>L本体</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation>Lボタン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation>R本体</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation>Rボタン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation>プレイヤー2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation>プレイヤー3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation>プレイヤー4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation>プレイヤー5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation>プレイヤー6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation>プレイヤー7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation>プレイヤー8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation>その他</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation>キーボード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation>拡張</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation>タッチスクリーン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation>マウス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation>モーション / タッチ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation>設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation>コントローラのデバッグ</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>入力設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation>コントローラの接続</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation>プロコントローラ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation>デュアルジョイコン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation>ジョイコン左</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation>ジョイコン右</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation>携帯</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation>入力デバイス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation>すべて</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation>キーボード / マウス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation>プロファイル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation>セーブ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation>新規</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation>削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>左スティック</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation>上</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation>左</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation>右</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation>下</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation>押下</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation>変更</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation>範囲</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation>デッドゾーン:0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation>変更範囲:0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation>Dパッド</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation>ZL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation>マイナス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation>キャプチャ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation>プラス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation>ホーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation>ZR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation>SL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation>SR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>ボタン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>右スティック</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation>デッドゾーン:%1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation>変更範囲:%1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation>[待機中]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation>モーション / タッチ設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation>モーション</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation>モーションプロバイダ:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation>感度:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation>タッチ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation>タッチプロバイダ:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation>キャリブレーション:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation>(100, 50) - (1800, 850)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation>設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation>ボタンマッピングの使用:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation>CemuhookUDP設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation>モーションとタッチ入力を提供するために、Cemuhook互換のUDP入力ソースを使用します。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation>サーバー:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation>ポート:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation>パッド:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation>パッド1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation>パッド2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation>パッド3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation>パッド4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation>詳細情報</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation>テスト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation>マウス(右クリック)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation>CemuhookUDP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation>エミュレータウィンドウ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;詳細情報&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation>テスト中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation>設定中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation>テスト成功</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation>サーバーからデータ受信成功</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation>テスト失敗</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation>サーバーから有効なデータを受信できませんでした。&lt;br&gt;サーバーが正しくセットアップされ、アドレスとポートが正しいことを確認してください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation>Citra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation>UDPテスト、またはキャリブレーション設定が実行中です。&lt;br&gt;完了するまでお待ちください。</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>マウス設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>マウスボタン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>進む:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>戻る:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>左:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>中央:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>右:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>消去</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation>デフォルト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[未設定]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>デフォルトに戻す</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[キーを押す]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation>ダイアログ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation>情報</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation>名称</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation>タイトルID</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation>ファイル名</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation>フォーマット</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation>バージョン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation>サイズ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation>開発者</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation>アドオン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation>全般</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation>システム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation>グラフィック</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation>拡張グラフィック</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation>オーディオ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation>プロパティ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation>共通設定を使用(%1)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation>パッチ名称</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation>バージョン</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>プロファイルマネージャ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>現在のユーザー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>ユーザー名</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>画像を設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>追加</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>リネーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>プロファイル管理はゲームが実行されていない場合のみ可能です。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>ユーザー名を入力</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>ユーザー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>新しいユーザーのユーザー名を入力:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>新しいユーザー名を入力:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>削除確認</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>ユーザー名”%1”を削除しようとしています。間違いありませんか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>ユーザー画像を選択 </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>JPEG画像 (*.jpg *.jpeg) </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>画像削除エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>既存画像の上書き時にエラーが発生しました: %1 </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>ファイル削除エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>ファイルを削除できませんでした: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>ユーザー画像ディレクトリ作成失敗</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>ユーザー画像保存ディレクトリ”%1”を作成できませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>ユーザー画像コピーエラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>画像を”%1”から”%2”へコピー出来ませんでした。</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>BCATは任天堂のゲームへのデータ送信方法であり、コミュニティに参加して追加コンテンツのロックを解除します。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>BCATバックエンド</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;BCAT, Boxcat, 現在のイベントについての詳細情報&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>Boxcatサービスはオフラインまたはインターネットに接続されていません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Boxcatイベントデータを処理中にエラー発生しました。yuzu開発者に連絡してください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>使用中のyuzuのバージョンは、サーバと比較して新しすぎまたは古すぎます。最新の公式リリースに更新してください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>Boxcatには現在イベントがありません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation>現在のBoxcatイベント</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>yuzuが最新のBoxcat状況を取得中です...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>システム設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation>リージョン:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation>自動</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation>デフォルト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation>CET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation>CST6CDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation>キューバ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation>EET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation>エジプト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation>アイルランド</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation>EST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation>EST5EDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation>GB-Eire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation>GMT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation>GMT+0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation>GMT-0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation>GMT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation>グリニッジ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation>香港</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation>HST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation>アイスランド</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation>イラン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation>イスラエル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation>ジャマイカ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation>日本</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation>クェゼリン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation>リビア</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation>MET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation>MST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation>MST7MDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation>ナバホ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation>NZ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation>NZ-CHAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation>ポーランド</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation>ポルトガル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation>PRC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation>PST8PDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation>ROC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation>ROK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation>シンガポール</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation>トルコ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation>UCT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation>ユニバーサル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation>UTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation>W-SU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation>WET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation>ズールー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation>USA</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation>ヨーロッパ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation>オーストラリア</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation>中国</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation>韓国</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation>台湾</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation>タイムゾーン:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>注意:地域設定が自動選択の場合、設定が上書きされる可能性があります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>日本(日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>英語</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>フランス(フランス語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>ドイツ(ドイツ語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>イタリア(イタリア語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>スペイン(スペイン語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>中国</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>韓国(韓国語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>オランダ(オランダ語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>ポルトガル(ポルトガル語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>ロシア(ロシア語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>台湾</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>イギリス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>カナダ・フランス語</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>ラテンアメリカ・スペイン語</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>簡体字中国語</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>繁体字中国語(正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>カスタムRTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>言語</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>RNG速度</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>モノラル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>ステレオ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>サラウンド</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>コンソールID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>サウンド出力モード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM yyyy h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>再作成</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>システム設定はゲームが実行されていない場合のみ可能です。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>現在の仮想スイッチが新しいものに置換されます。現在の仮想スイッチは復旧できません。ゲームに予期せぬ影響を与える可能性があります。古い設定などを使うと失敗するかもしれません。続行しますか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>警告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>コンソールID: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation>タッチスクリーンマッピング設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation>マッピング:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation>新規</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation>削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation>リネーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation>下の領域をクリックしてポイントを追加し、ボタンを押してバインドします。
+ポイントをドラッグして位置を変更するか、テーブルのセルをダブルクリックして値を編集します。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation>ポイント削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation>ボタン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation>新規プロファイル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation>新規プロファイルの名称を入力</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation>プロファイル削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation>プロファイル%1を削除しますか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation>プロファイルリネーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation>新規名称:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation>[キーを押す]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>タッチスクリーン設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>警告:このページの設定は、yuzuのタッチスクリーンエミュレートの内部動作に影響します。これらを変更すると、タッチスクリーンが部分的に機能しなくなったりするなど、望ましくない動作が生じる可能性があります。それらを理解した上で、このページを使用してください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>タッチパラメータ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>タッチ直径Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>指</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>タッチ直径X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>回転角</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>デフォルトに戻す</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>全般</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>注意:言語を変更した時点で、設定が適用されます。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>UI言語:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>テーマ:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>ゲームリスト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>アドオン列を表示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>アイコンサイズ:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>1行目:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>2行目:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation>スクリーンショット</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation>スクリーンショットの保存場所を確認する(Windowsのみ)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation>スクリーンショットパス:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation>スクリーンショットパスを選択...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>英語</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>フォーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>yuzu Webサービス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>ユーザー名とトークンを入力すると、yuzuがユーザー識別情報を含む可能性がある追加の統計情報データを収集することを許可することに同意したと見なされます。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>検証</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>サインアップ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>トークン:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>ユーザー名:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>私のトークンはなんですか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>テレメトリ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>匿名統計情報データをyuzuチームと共有</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>詳細情報</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>テレメトリID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>再作成</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Discordプレゼンス</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Discordステータスに実行中のゲームを表示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;詳細情報&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;サインアップ&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;私のトークンはなんですか?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>テレメトリID:0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>未設定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>トークンは検証されていません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>トークンは検証されていません。トークンの変更はまだ保存されていません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>検証中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>検証失敗</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>検証に失敗しました。トークンを正しく入力したこと、およびインターネット接続が機能していることを確認してください。</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>yuzuを改善するための&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;匿名データが収集されました&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;統計情報データを共有しますか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>テレメトリ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>テキストチェック失敗</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>Webアプレットをロード中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Webアプレットを終了する</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>終了</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Webアプリケーションを終了するには、ゲームが提供する&quot;終了&quot;を選択するか、メニューの&quot;Webアプレットを終了する&quot;を選択するか、&quot;Enter&quot;キーを押してください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Webアプレット</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>使用中のyuzuはQtWebEngineをサポートしていないため、ゲームの説明書やWebページを正しく表示できません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation>ビルド中のシェーダー数</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>現在のエミュレーション速度。値が100%より高いか低い場合、エミュレーション速度がSwitchより速いか遅いことを示します。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>ゲームが現在表示している1秒あたりのフレーム数。これはゲームごと、シーンごとに異なります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Switchフレームをエミュレートするのにかかる時間で、フレームリミットやV-Syncは含まれません。フルスピードエミュレーションの場合、最大で16.67ミリ秒になります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation>DOCK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation>ASYNC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation>MULTICORE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation>VULKAN</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation>OPENGL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>最近使用したファイルを消去</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>古いゲームフォーマットの警告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>このゲームでは、分解されたROMディレクトリフォーマットを使用しています。これは、NCA、NAX、XCI、またはNSPなどに取って代わられた古いフォーマットです。分解されたROMディレクトリには、アイコン、メタデータ、およびアップデートサポートがありません。&lt;br&gt;&lt;br&gt;yuzuがサポートするSwitchフォーマットの説明については、&lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;wikiをチェックしてください&lt;/a&gt;。このメッセージは二度と表示されません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>ROMロード中にエラーが発生しました!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>このROMフォーマットはサポートされていません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>ビデオコア初期化中にエラーが発生しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>ビデオコアの実行中にyuzuでエラーが発生しました。詳細については、ログを参照してください。ログへのアクセスの詳細については、次のページを参照してください:&lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;ログファイルをアップロードする方法&lt;/a&gt;。最新のグラフィックドライバを使用していることを確認して下さい。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation>ROMロードエラー!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>不明なエラーが発生しました。詳細はログを確認して下さい。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>開始</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>データのセーブ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Modデータ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>”%1”フォルダを開けませんでした</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>フォルダが存在しません!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>シェーダキャッシュを開けませんでした</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>このタイトル用のシェーダキャッシュは存在しません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation>コンテンツ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation>アップデート</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation>DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation>エントリ削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation>インストールされているゲーム%1を削除しますか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation>削除成功</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation>インストールされたゲームを正常に削除しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation>%1削除エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation>ゲームはNANDにインストールされておらず、削除できません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation>インストールされたアップデートを正常に削除しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation>このタイトルのアップデートはインストールされていません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation>このタイトルにはDLCがインストールされていません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation>%1にインストールされたDLCを正常に削除しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation>転送可能なシェーダーキャッシュを削除しますか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation>カスタムゲーム設定を削除しますか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation>ファイル削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation>転送可能なシェーダーキャッシュの削除エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation>転送可能なシェーダーキャッシュが正常に削除されました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation>転送可能なシェーダーキャッシュを削除できませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation>カスタム設定の削除エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation>このタイトルのカスタム設定は存在しません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation>カスタムゲーム設定を正常に削除しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation>カスタムゲーム設定の削除に失敗しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>RomFSの解析に失敗しました!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>RomFSファイルをコピー中にエラーが発生したか、ユーザー操作によりキャンセルされました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>フル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>スケルトン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>RomFSダンプモードの選択</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>RomFSのダンプ方法を選択してください。&lt;br&gt;”完全”はすべてのファイルが新しいディレクトリにコピーされます。&lt;br&gt;”スケルトン”はディレクトリ構造を作成するだけです。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>RomFSを解析中・・・</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>キャンセル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>RomFS解析成功!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>操作は成功しました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>”%1”を開けませんでした</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>ディレクトリの選択</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>プロパティ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>ゲームプロパティをロード出来ませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Switch実行ファイル (%1);;すべてのファイル (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>ファイルのロード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>展開されているROMディレクトリを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>無効なディレクトリが選択されました</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>選択されたディレクトリに”main”ファイルが見つかりませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation>インストール可能なスイッチファイル (*.nca *.nsp *.xci);;任天堂コンテンツアーカイブ (*.nca);;任天堂サブミッションパッケージ (*.nsp);;NXカートリッジイメージ (*.xci)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation>ファイルのインストール</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>&quot;%1&quot;ファイルをインストールしています・・・</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation>インストール結果</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>システムアプリケーション</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>システムアーカイブ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>システムアプリケーションアップデート</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>ファームウェアパッケージ(Type A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>ファームウェアパッケージ(Type B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>ゲーム</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>ゲームアップデート</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>ゲームDLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>差分タイトル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>NCAインストール種別を選択・・・</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>インストールするNCAタイトル種別を選択して下さい:
+(ほとんどの場合、デフォルトの”ゲーム”で問題ありません。)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>インストール失敗</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>選択されたNCAのタイトル種別が無効です。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>ファイルが存在しません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>ファイル”%1”が存在しません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>続行</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>表示エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>yuzuアカウントが存在しません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>ゲームの互換性テストケースを送信するには、yuzuアカウントをリンクする必要があります。&lt;br&gt;&lt;br/&gt;yuzuアカウントをリンクするには、エミュレーション > 設定 > Web から行います。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation>URLオープンエラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation>URL&quot;%1&quot;を開けません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>amiiboファイル (%1);;すべてのファイル (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>amiiboのロード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>amiiboデータファイルを開けませんでした</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>amiiboデータファイル”%1”を読み込めませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>amiiboデータファイルを読み込み中にエラーが発生した</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>amiiboデータを完全には読み取ることができませんでした。%1バイトを読み込もうとしましたが、%2バイトしか読み取れませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>amiiboデータ読み込み中にエラーが発生しました</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>amiiboデータをロードできませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>スクリーンショットのキャプチャ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG画像 (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>速度:%1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>速度:%1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>ゲーム:%1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>フレーム:%1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>ロードしようとしているゲームはプレイする前にスイッチからダンプされた追加のファイルを必要とします。&lt;br/&gt;&lt;br/&gt;これらのファイルのダンプの詳細については、次のWikiページを参照してください:&lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;スイッチコンソールからのシステムアーカイブと共有フォントをダンプする&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;ゲームリストに戻りますか?エミュレーションを続けると、クラッシュ、保存データの破損、またはその他のバグが発生する可能性があります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>yuzuはSwitchのシステムアーカイブ &quot;%1&quot; を見つけられませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>yuzuはSwitchのシステムアーカイブ &quot;%1&quot; &quot;%2&quot; を見つけられませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>システムアーカイブが見つかりません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>システムアーカイブが見つかりません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>yuzuはSwitchの共有フォント &quot;%1&quot; を見つけられませんでした。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>共有フォントが存在しません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>共有フォントが存在しません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>致命的なエラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzuが致命的なエラーを検出しました。詳細については、ログを参照してください。ログへのアクセスの詳細については、次のページを参照してください。&lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;ログファイルをアップロードする方法&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;ゲームリストに戻りますか?エミュレーションを続けると、クラッシュ、保存データの破損、またはその他のバグが発生する可能性があります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>致命的なエラー発生</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>キーの再取得確認</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>すべてのキーを再作成しようとしています。
+これが何を意味するのか分からない場合、または何をしようとしているのか分からない場合、
+これは破壊的な可能性がある実行です。
+これがあなたが望むものであることを確認し、
+そしてオプションでバックアップを作成します。
+
+これにより、自動生成されたキーファイルが削除され、キー導出モジュールが再実行されます。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation>ヒューズがありません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation> - BOOT0がありません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation> - BCPKG2-1-Normal-Mainがありません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation> - PRODINFOがありません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation>派生コンポーネントがありません</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation>コンポーネントが見つからず、キーの導出が完了しない可能性があります。&lt;br&gt;ゲームとキーを取得するために、&lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;yuzuクイックスタートガイド&lt;/a&gt;の手順に従って下さい。&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>キーを作成中...
+システムのパフォーマンスによっては
+1分以上かかります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>派生キー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>RomFSダンプターゲットの選択</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>ダンプしたいRomFSを選択して下さい。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>yuzuを終了してもよろしいですか?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>エミュレーションを停止してもよろしいですか?セーブされていない進行状況は失われます。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>現在動作中のアプリケーションはyuzuに終了しないよう要求しています。
+
+無視してとにかく終了しますか?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation>OpenGLは使用できません!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation>yuzuはOpenGLサポート付きでコンパイルされていません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation>Vulkanは使用できません!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation>yuzuはVulkanサポート付きでコンパイルされていません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation>OpenGL4.3初期化エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation>GPUがOpenGL 4.3をサポートしていないか、最新のグラフィックスドライバーではありません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation>OpenGL初期化エラー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation>GPUが1つ以上の必要なOpenGL拡張機能をサポートしていない可能性があります。最新のグラフィックドライバを使用していることを確認してください。&lt;br&gt;&lt;br&gt;サポートされていない拡張機能:&lt;br&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>名称</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>互換性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>アドオン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>ファイル種別</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>サイズ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>セーブデータディレクトリを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Modデータディレクトリを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>シェーダキャッシュを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation>削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation>インストールされているアップデートを削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation>全てのインストールされているDLCを削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation>シェーダーキャッシュを削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation>カスタム設定を削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation>全てのインストールされているコンテンツを削除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>RomFSをダンプ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>タイトルIDをクリップボードへコピー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>GameDBエントリを表示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>プロパティ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>サブフォルダをスキャンする</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>ゲームディレクトリを削除する</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation>▲ 上へ移動</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation>▼ 下へ移動</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>ディレクトリの場所を開く</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>パーフェクト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>ゲームはオーディオ、またはグラフィックの不具合なしで完璧に動作し、回避策なしで期待通りに動作します。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>グレート</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>ゲームの動作にはグラフィック、またはオーディオの軽微な不具合がありますが、最初から最後までプレイ可能です。いくつかの回避策が必要な場合があります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>OK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>ゲームの動作にはグラフィック、またはオーディオの重大な不具合がありますが、回避策を使うことで最初から最後までプレイ可能です。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>NG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>ゲームは動作しますが、グラフィック、またはオーディオに重大な不具合があります。回避策を使用しても不具合が原因で特定の場所から進めなくなります。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>イントロ/メニュー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>グラフィック、またはオーディオの重大な不具合のため、ゲームはプレイ不可能です。スタート画面から先に進めることは出来ません。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>起動せず</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>ゲームは起動時にクラッシュしました。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>未テスト</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>ゲームはまだテストされていません。</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>新しいゲームリストフォルダを追加するにはダブルクリックしてください。</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>フィルター:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>フィルターパターンを入力</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation>これらがインストールするファイルであることを確認してください。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation>アップデート、またはDLCをインストールすると、以前にインストールしたものが上書きされます。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation>インストール</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation>ファイルをNANDへインストール</translation>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>シェーダをロード中 387 / 1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>シェーダをロード中 %v / %m</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>予想時間 5分4秒</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>ロード中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>シェーダをロード中 %1 / %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>起動中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>予想時間 %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>ファイル(&amp;F)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>最近使用したファイル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>エミュレーション(&amp;E)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>表示(&amp;V)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>デバッグ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>ツール</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>ヘルプ(&amp;H)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation>ファイルをNANDへインストール...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>ファイルをロード...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>フォルダをロード...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>終了(&amp;E)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>実行(&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>中断(&amp;P)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>停止(&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>キーの再初期化...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>yuzuについて</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>単一ウィンドウモード</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>設定...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>ドックウィジェットヘッダーの表示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>フィルターバーの表示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>ステータスバーの表示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation>ウィンドウサイズをリセット</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>フルスクリーン</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>再起動</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>amiiboをロード...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>互換性を報告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation>Modページを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation>クイックスタートガイドを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation>FAQ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>yuzuディレクトリを開く</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>スクリーンショットをキャプチャ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation>現在のゲームの設定...</translation>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>マイクロプロファイル</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>インストール済みSDタイトル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>インストール済みNANDタイトル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>システムタイトル</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>新しいゲームディレクトリを追加する</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[未設定]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>十字キー %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>軸 %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>ボタン %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[不明]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation>クリック0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation>クリック1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation>クリック2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation>クリック3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation>クリック4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation>GC Axis %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation>GC Button %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[未使用]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>軸 %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation>GC Axis %1</translation>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>エラーが発生しました。
+もう一度試す、またはソフト開発者に連絡してください。
+
+エラーコード: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>%2 の %1 でエラーが発生しました。
+もう一度試す、またはソフト開発者に連絡してください。
+
+エラーコード: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>エラーが発生しました。
+エラーコード: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>ユーザー選択:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>ユーザー</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>プロファイル選択</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>テキストを入力:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>ソフトウェアキーボード</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>ホットキーを入力</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>コールスタック</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>ミューテックス 0x%1 待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>待機:%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>オーナーハンドル: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>全オブジェクト待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>以下のオブジェクトのうちの一つを待機中</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation>[%1]%2 %3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation>スレッドなしで待機</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>実行中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>準備完了</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>中断</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>HLEリターン待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>休止中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>IPC返答待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>オブジェクト待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>ミューテックス待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>条件変数待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>アドレス決定待ち</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>休止</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>死亡</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation> PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>理想的</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>コア %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>不明なプロセッサ %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>プロセッサ = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>イデアルコア = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>アフィニティマスク = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>スレッドID = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>優先度 = %1(現在) / %2(通常)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>最終実行刻み = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>ミューテックス待ちではない</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>スレッドによる待機</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>待ちツリー</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/nl.ts b/dist/languages/nl.ts
new file mode 100644
index 000000000..0f5178458
--- /dev/null
+++ b/dist/languages/nl.ts
@@ -0,0 +1,4719 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="nl" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>Over yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu is een experimentele open-bron emulator voor de Nintendo Switch, in licentie gegeven onder GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;De software zou niet gebruikt moeten worden om games te spelen die je niet legaal verkregen hebt.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Broncode&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Bijdragers&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Licentie&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is een handelsmerk van Nintendo. yuzu is op geen enkele manier met Nintendo verbonden.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>Communiceren met de server...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>Annuleren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Rapporteer Compatibiliteit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Rapporteer Game Compatibiliteit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Als je kiest een test case op te sturen naar de &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu compatibiliteitslijst&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, zal de volgende informatie worden verzameld en getoond op de site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Informatie (CPU / GPU / Besturingssysteem)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Welke versie van yuzu je draait&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Het verbonden yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Perfect</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;De game werkt foutloos, zonder geluid of grafische problemen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Goed</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game werkt met kleine grafische of geluid problemen en is speelbaar van start tot eind. Kan enkele tijdelijke oplossingen nodig hebben.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>OK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game werkt met grote grafische of geluid problemen, maar is speelbaar van start tot eind met tijdelijke oplossingen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Slecht</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game werkt, maar met grote grafische of geluid problemen. Specifieke gebieden kunnen niet worden uitgespeeld vanwege problemen, zelfs met tijdelijke oplossingen. &lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is compleet onspeelbaar dankzij grote grafische of geluid problemen. Onmogelijk voorbij het startscherm te komen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Start Niet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;De game crasht wanneer je het probeert op te starten.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Onafhankelijk van snelheid of prestaties, Hoe goed speelt de game van start tot eind op deze versie van yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Bedankt voor je inzending!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Inzenden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Communicatiefout</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Er was een fout tijdens het insturen van de Testcase.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Volgende</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Geluid</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Output Engine:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Dit nabewerkings effect past de snelheid van het geluid zodanig aan dat de snelheid van de emulatie en van het geluid op elkaar afgestemd zijn. Dit zorgt er wel voor dat de latency van het geluid hoger word.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Geluid Rekking Aanzetten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Geluid Apparaat:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Volume:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>GDB Stub Aanzetten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Poort:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Loggen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Globale Log Filter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Laat Log Venster Zien (Alleen voor Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Open Log Locatie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Argumenten Rij</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Gebruik Uitgebreide Rapporteer Services</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Deze optie hersteld wanneer je yuzu afsluit.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Geavanceerd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Kiosk (Quest) Modus</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>yuzu Configuratie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Algemeen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>UI</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Game Lijst</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>Systeem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Profielen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Bestandssysteem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Bediening</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Sneltoetsen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Debug</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Grafisch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Geluid</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Services</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Opslag Folders</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>SD Kaart</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Gamekaart</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Pad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Ingevoerd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Huidig Spel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Patch Beheer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Dump Uitgepakte NSO&apos;s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>Dump ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Mod Laad Root</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>Dump Root</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Caching</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Cache Folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>Cache Spel Lijst Metadata</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Herstel Metadata Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Selecteer Geëmuleerde NAND Folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Selecteer Geëmuleerde SD Folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>Selecteer Gamekaart Pad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Selecteer Dump Folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Selecteer Mod Laad Folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Selecteer Cache Folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>De metadata cache is al leeg.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>De operatie is succesvol voltooid.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>De metadata cache kon niet worden verwijderd. Het wordt mogelijk gebruikt of bestaat niet.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Algemeen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Limiteer Snelheid Percentage</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Bevestig sluiten terwijl emulatie nog bezig is</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>Vraag voor gebruiker bij het opstartten van het spel.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Pauzeer Emulatie wanneer yuzu op de achtergrond openstaat</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Gebruik schijf shader cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Gebruik asynchroon GPU emulatie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Achtergrondkleur:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Sneltoets Instellingen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>Dubbel-klik op een binding om het te veranderen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Actie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Sneltoets</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Context</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Ongeldige Toets Volgorde</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Speler 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Speler 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Speler 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Speler 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Speler 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Speler 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Speler 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Speler 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Geavanceerd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Configureer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configureer Invoer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Linker Stick</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Gezicht Knoppen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Rechter Stick</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Configureer Muis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Muisknoppen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Voorwaarts:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Terug:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Links:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Middel:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Rechts:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Herstellen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[niet ingesteld]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Herstel Standaardwaarde</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[druk op knop]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Profiel Beheer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Huidige Gebruiker</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Gebruikersnaam</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Selecteer Afbeelding</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Voeg Toe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Hernoem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Verwijder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>Profiel beheer is alleen beschikbaar wanneer het spel niet bezig is.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Voer een Gebruikersnaam in</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Gebruikers</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Voer een gebruikersnaam in voor de nieuwe gebruiker:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Voer nieuwe gebruikersnaam in:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Bevestig Verwijdering</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Je staat op het punt een gebruiker met de naam &quot;%1&quot; te verwijderen. Weet je het zeker?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Selecteer gebruiker&apos;s foto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>JPEG foto&apos;s (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Fout tijdens verwijderen afbeelding</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Er is een fout opgetreden bij het overschrijven van de vorige afbeelding in: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Fout tijdens verwijderen bestand</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Kan bestaand bestand niet verwijderen: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Fout tijdens het maken van de map met afbeeldingen van de gebruiker</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Fout tijdens het maken van map %1 om gebruikersafbeeldingen in te bewaren.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Fout tijdens het kopiëren van de gebruiker afbeelding</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Kan afbeelding niet kopiëren van %1 naar %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>BCAT is Nintendo&apos;s manier om informatie naar games te versturen om de gemeenschap te stimuleren of om nieuwe inhoud te ontgrendelen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>BCAT Backend</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Leer meer over BCAT, Boxcat, en actuele Evenementen&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>De boxcat service is offline of je bent niet verbonden met het internet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Er was een fout tijdens het processen van de boxcat evenement data. Contacteer de yuzu ontwikkelaars.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>De versie van yuzu die je gebruikt is te nieuw of te oud voor de server. Probeer te updaten naar de nieuwste officiële release van yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>Er zijn momenteel geen evenementen op boxcat.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>Yuzu haalt de laatste boxcat-status op...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Systeeminstellingen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Noot: dit kan worden overschreven wanneer de regio instelling op automatisch selecteren staat.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Japans (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Engels (English)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Frans (Français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Duits (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Italiaans (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Spaans (Español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Chinees (正體中文 / 简体中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Koreaans (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Nederlands (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Portugees (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Russisch (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Taiwanese</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Brits Engels</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Canadees Frans</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Latijns Amerikaans Spaans</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Vereenvoudigd Chinees</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Traditioneel Chinees (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>Handmatige RTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Taal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>RNG Seed</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Stereo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Surround</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>Console ID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Geluid uitvoer mode</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM jjjj u:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Herstel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>Systeeminstellingen zijn enkel toegankelijk wanneer er geen game draait.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Dit vervangt je huidige virtuele Switch met een nieuwe. Je huidige virtuele Switch kan dan niet meer worden hersteld. Dit kan onverwachte effecten hebben in spellen. Dit werkt niet als je een oude config savegame gebruikt. Doorgaan?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Waarschuwing</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>Console ID: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Configureer Touchscreen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Waarschuwing: Instellingen in deze pagina hebben invloed op de interne werking van yuzu&apos;s geemuleerde touchscreen. Veranderingen kunnen ongewenste resultaten hebben, zoals ervoor zorgen dat het touchscreen half of niet werkt. Gebruik deze pagina enkel als je weet wat je doet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Touch Parameters</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Touch Diameter Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Vinger</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Touch Diameter X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Rotatiehoek</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Herstel Standaardwaardes</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>Algemeen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Notitie: De taal veranderen past uw configuratie toe.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Interface taal:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Thema:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Game Lijst</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Toon Add-Ons Kolom</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Icoon Grootte:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Rij 1 Text:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Rij 2 Text:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Engels</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Vorm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>yuzu Web Service</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Door je gebruikersnaam en token te geven, ga je akkoord dat yuzu extra gebruiksdata verzameld, waaronder mogelijk gebruikersidentificatie-informatie.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Verifieer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Registreer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Gebruikersnaam:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>Wat is mijn token?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Telemetrie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Deel anonieme gebruiksdata met het yuzu team</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Leer meer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>Telemetrie ID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Regenereer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Discord Presence</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Toon huidige game in je Discord status</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Leer meer&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Registreer&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Wat is mijn token?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>Telemetrie ID: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Niet gespecificeerd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>Token niet geverifieerd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>Token is niet geverifieerd. De verandering aan uw token zijn niet opgeslagen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Verifiëren...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Verificatie mislukt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Verificatie mislukt. Check dat uw token correct is en dat uw internet werkt.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Annonieme gegevens worden verzameld&lt;/a&gt; om yuzu te helpen verbeteren. &lt;br/&gt;&lt;br/&gt; Zou je jouw gebruiksgegevens met ons willen delen?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Telemetrie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Tekst Check Is Gefaald</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>Web Applet Laden...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Web Applet Afsluit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Afsluiten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Om de webtoepassing af te sluiten, gebruik de bedieningselementen van het spel om afsluiten te selecteren, selecteer de optie &apos;Web Applet Afsluiten&apos; in de menubalk of druk op de &apos;Enter&apos; toets.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Deze versie van yuzu is gebouwd zonder ondersteuning van QtWebEngine, wat betekent dat yuzu de gevraagde spelhandleiding of webpagina niet correct kan weergeven.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Huidige emulatie snelheid. Waardes hoger of lager dan 100% betekent dat de emulatie sneller of langzamer loopt dan de Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Hoeveel frames per seconde de game op dit moment weergeeft. Dit zal veranderen van game naar game en van scène naar scène.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Tijd gebruikt om een frame van de Switch te emuleren, waarbij framelimiteren of v-sync niet wordt meegerekend. Voor emulatie op volledige snelheid zou dit maximaal 16.67 ms zijn.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Recente Bestanden Verwijderen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Waarschuwing Verouderd Spel Formaat</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Je gebruikt gedeconstrueerd ROM map formaat voor dit Spel, dit is een verouderd formaat en is vervangen door formaten zoals NCA, NAX, XCI of NSP. Gedeconstrueerd ROM map heeft geen iconen, metadata en update understeuning.&lt;br&gt;&lt;br&gt;Voor een uitleg over welke Switch formaten yuzu ondersteund, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;kijk op onze wiki&lt;/a&gt;. Dit bericht word niet nog een keer weergegeven.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>Fout tijdens het laden van een ROM!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>Het formaat van de ROM is niet ondersteunt.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Er is een fout opgetreden tijdens het initialiseren van de videokern.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu is een fout tegengekomen tijdens het uitvoeren van de video kern, kijk alstublieft naar de log for meer details. Voor meer informatie op het vinden van de log, kijk alstublieft naar de volgende pagina : &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;hoe upload je een log bestand&lt;/a&gt;. Bevestig dat je dat laaste grafische driver hebt voor je GPU.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Een onbekende fout heeft plaatsgevonden. Kijk in de log voor meer details.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Start</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Save Data</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Mod Data</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Fout tijdens het openen van %1 folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>Folder bestaat niet!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Fout Bij Het Openen Van Overdraagbare Shader Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>Er bestaat geen shader cache voor deze game</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>RomFS Extractie Mislukt!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Er was een fout tijdens het kopiëren van de RomFS bestanden of de gebruiker heeft de operatie geannuleerd.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Vol</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>Skelet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Selecteer RomFS Dump Mode</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Selecteer alstublieft hoe je de RomFS wilt dumpen.&lt;br&gt;Volledig kopieërd alle bestanden in een map terwijl &lt;br&gt; skelet maakt alleen het map structuur.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>RomFS uitpakken...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Annuleren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>RomFS Extractie Geslaagd!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>De operatie is succesvol voltooid.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Fout bij openen %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Selecteer Map</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Eigenschappen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>De eigenschappen van de game kunnen niet geladen worden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Switch Executable (%1);;Alle bestanden (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Laad Bestand</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Open Gedecomprimeerd ROM Map</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Ongeldige Map Geselecteerd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>De map die je hebt geselecteerd bevat geen &apos;main&apos; bestand.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Bestand &quot;%1&quot; Installeren...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Systeem Applicatie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Systeem Archief</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Systeem Applicatie Update</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Filmware Pakket (Type A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Filmware Pakket (Type B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Game</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Game Update</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>Game DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Delta Titel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Selecteer NCA Installatie Type...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Selecteer het type titel hoe je wilt dat deze NCA installeerd:
+(In de meeste gevallen is de standaard &apos;Game&apos; juist.)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Installatie Mislukt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>Het type title dat je hebt geselecteerd voor de NCA is ongeldig.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Bestand niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Bestand &quot;%1&quot; niet gevonden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Doorgaan</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Fout Weergave</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Je yuzu account mist</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Om game campatibiliteit te raporteren, moet je je yuzu account koppelen.&lt;br&gt;&lt;br/&gt; Om je yuzu account te koppelen, ga naar Emulatie &amp;gt; Configuratie &amp;gt; Web.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Amiibo Bestand (%1);; Alle Bestanden (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Laad Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Fout tijdens het openen van het Amiibo gegevens bestand</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Kan Amiibo bestand &quot;%1&quot; niet lezen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Fout tijdens het lezen van het Amiibo gegevens bestand</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Kan de volledige Amiibo gegevens niet lezen. Verwacht om %1 bytes te lezen, maar het is alleen mogelijk om %2 bytes te lezen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Fout tijdens het laden van de Amiibo data</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Kan de Amiibo gegevens niet laden.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Screenshot Vastleggen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG afbeelding (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Snelheid: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Snelheid: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Game: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Frame: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>De game die je probeert te laden heeft extra bestanden nodig van je Switch voordat je het kan spelen. &lt;br/&gt;&lt;br/&gt;Voor meer informatie over het dumpen van deze bestanden, volg alsjeblieft onze wiki pagina: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Het dumpen van Systeem Archieven en de Gedeelde Lettertypen van een Switch console &lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;Wil je terug gaan naar de game lijst? Verdergaan met de emulatie zal misschien gevolgen hebben als vastlopen, beschadigde opslag data, of andere problemen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>yuzu was niet in staat om de Switch systeem archieven te vinden. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>yuzu was niet in staat om de Switch systeem archieven te vinden. %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Systeem Archief Niet Gevonden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Systeem Archief Mist</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>yuzu was niet in staat om de Switch shared fonts te vinden. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Shared Fonts Niet Gevonden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Gedeelde Lettertypes Niet Gevonden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Fatale Fout</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu is een fatale fout tegengekomen, zie de log voor meer details. Voor meer informatie over toegang krijgen tot de log, zie de volgende pagina: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Hoe upload je een log bestand&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Zou je terug willen naar de game lijst? Doorgaan met emulatie kan resulteren in vastlapen, corrupte save gegevens, of andere problemen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Fatale Fout opgetreden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Bevestig Sleutel Herafleiding</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Je bent op het punt al je sleutels geforceerd opnieuw te verkrijgen.
+Als je niet weet wat dit doet of wat je aan het doen bent,
+dit is potentieel een vernietigende actie.
+Zorg ervoor dat je zeker weet dat dit is wat je wilt doen
+en optioneel maak backups.
+
+Dit zal je automatisch gegenereerde sleutel bestanden verwijderen en de sleutel verkrijger module opnieuw starten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Dit zal misschien een paar minuten duren gebaseerd
+op je systeem&apos;s performatie.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Sleutels afleiden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Selecteer RomFS Dump Doel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Selecteer welke RomFS je zou willen dumpen.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Weet je zeker dat je yuzu wilt sluiten?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Weet je zeker dat je de emulatie wilt stoppen? Alle onopgeslagen voortgang will verloren gaan.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>De momenteel actieve toepassing heeft yuzu gevraagd om niet af te sluiten.
+
+Wilt u dit omzeilen en toch afsluiten?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Naam</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Compatibiliteit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Toevoegingen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Bestands type</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Grootte</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Open Locatie Van Save Gegevens </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Open Mod Data Locatie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Open Overdraagbare Shader Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Dump RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Kopieer Titel ID naar Klembord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Navigeer naar GameDB inzending</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Eigenschappen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Scan Subfolders</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Verwijder Game Directory</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Open Directory Locatie</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Perfect</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>Game functioneerd foutloos, zonder auditieve of grafische glitches. Alle geteste functionaliteit werkt zoals bedoeld zonder dat er tijdelijke oplossingen nodig zijn.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Goed</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>Game werkt met kleine grafische of auditieve glitches en is speelbaar van start tot eind. Kan enkele workarounds nodig hebben.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Okay</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>Game werkt met grove grafische of auditieve glitches, maar is speelbaar van start tot eind met workarounds</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Slecht</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>Game werkt, maar met grove grafische of auditieve glitches. Specifieke gebieden kunnen niet worden uitgespeeld vanwege glitches, zelfs met workarounds.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>Game is compleet onspeelbaar dankzij grove grafische of auditieve glitches. Onmogelijk voorbij het startscherm te gaan.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Start Niet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>De Game crasht wanneer hij probeert op te starten.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Niet Getest</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>Deze Game is nog niet getest.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Dubbel-klik om een ​​nieuwe map toe te voegen aan de lijst met games</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filter:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Voer patroon in om te filteren:</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>Shaders Laden 387 / 1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>%v Shaders Laden van de %m</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Geschatte Tijd 5m 4s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>Laden...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>Shaders Laden %1 / %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Starten...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Geschatte Tijd %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Bestand</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Recente Bestanden</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Emulatie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Weergeven</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Debugging</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Hulpmiddelen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Help</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Laad Bestand...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Laad Folder...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>A&amp;fsluiten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pauzeren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Sleutels opnieuw initialiseren...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>Over yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Enkel Venster Modus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Configureren...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Dock Widget Rubriek Weergeven</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Laat filter balk zien</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Laat status balk zien</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Volledig Scherm</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Herstart</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Laad Amiibo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Raporteer Compatibiliteit</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Open yuzu Folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Screenshot Vastleggen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroProfiel</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Geïnstalleerde SD Titels</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Geïnstalleerde NAND Titels</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Systeem Titels</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Voeg Nieuwe Game Map Toe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[niet aangegeven]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Hat %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Axis %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Knop %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[onbekend]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[ongebruikt]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Axis %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>Er is een fout opgetreden.
+Probeer het opnieuw of neem contact op met de ontwikkelaar van de software.
+
+Fout Code: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>Er is een fout opgetreden bij %1 en %2.
+Probeer het opnieuw of neem contact op met de ontwikkelaar van de software.
+
+Fout Code: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>Er is een fout opgetreden.
+Fout Code: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Selecteer een gebruiker:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Gebruikers</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Profiel keuzeschakelaar</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Voer tekst in:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Software Toetsenbord</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Voer een hotkey in</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Call stack</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>wachten op mutex 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>heeft wachtende: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>eigenaar handvat: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>wachten op alle objecten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>wachten op een van de volgende objecten</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>Bezig met uitvoeren</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>klaar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>gepauzeerd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>wachten op HLE terugkeer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>slapen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>wachten op IPC antwoord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>wachten op objecten</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>wachten op mutex</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>wachten op conditie variabele</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>wachten op adres arbiter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>slapend</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>dood</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation> PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>ideaal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>kern %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Onbekende processor %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>processor = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>ideale kern = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>affiniteit masker = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>draad id = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>prioriteit = %1(huidige) / %2(normaal)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>laatste lopende ticks = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>Niet wachtend op mutex</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>Wachtend door draad</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Wacht Boom</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/pl.ts b/dist/languages/pl.ts
new file mode 100644
index 000000000..4cd9b6fca
--- /dev/null
+++ b/dist/languages/pl.ts
@@ -0,0 +1,4713 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="pl" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>O yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu jest eksperymentalnym emulatorem o otwartym kodzie źródłowym dla Nintendo Switch w ramach licencji GLPv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;To oprogramowanie nie powinno być używane do gier, których nie nabyłeś legalnie.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Strona&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Kod źródłowy&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Współautorzy&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Licencja&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; jest znakiem towarowym Nintendo. yuzu nie jest stowarzyszony w żaden sposób z Nintendo.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>Anuluj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>Konfiguracja zakończona!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Zgłoś kompatybilność</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Zgłoś kompatybilność gry</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Jeśli postanowisz wysłać wyniki testu na &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;listę kompatybilności yuzu&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, następujące informacja zostaną zebrane i wyświetlone na stronie:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Informacja o sprzęcie komputerowym (procesor / karta graficzna / system operacyjny)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Wersja yuzu, której używasz&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Połączone konto yuzu&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Idealna</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Gra działa bez zarzutu, bez błędów graficznych lub dźwiękowych.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Świetna</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Gra działa z pomniejszymi błędami dźwiękowymi lub graficznymi, jest grywalna od początku do końca. Może wymagać kilku obejść/poprawek.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>W porządku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Gra działa z większymi błędami dźwiękowymi lub graficznymi, ale jest grywalna od początku do końca. Może wymagać kilku obejść/poprawek.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Zła</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Gra działa z większymi błędami dźwiękowymi lub graficznymi. Niemożliwe jest przejście konkretnych miejsc nawet z obejściami.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Gra w żadnym stopniu nie jest grywalna ze względu na poważne błędy graficzne lub dźwiękowe. Niemożliwe jest przejście ekranu początkowego.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Nie uruchomia się</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Gra zawiesza się podczas próby uruchomienia się.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Pomijając prędkość lub wydajność, jak dobrze ta gra zachowuje się w tej wersji yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Dziękujemy za Twoją opinię!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Wysyłanie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Błąd komunikacyjny</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Wystąpił błąd podczas wysyłania wyników testu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Następny</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Dźwięk</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Silnik wyjścia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Ten efekt post-przetwarzania dostosowuje prędkość dźwięku tak, aby zgadzała się z dźwiękiem emulowanym pomagając przy tym zapobiec zacinaniu się dźwięku. Zwiększa jednak opóźnienie dźwięku.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Włącz rozciąganie dźwięku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Urządzenie dźwiękowe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>Ustaw głośność:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Głośność</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>Ogólne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>Niebezpieczne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>Włącz Debug Mode</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Ustawienia CPU są dostępny gdy gra nie jest uruchomiona </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>Włącz namiastkę GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Port</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Logowanie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Globalny filtr rejestrów</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Pokaż okno rejestrów (tylko Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Otwórz miejsce rejestrów</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Linijka argumentu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Zaawansowane</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation>Wyczyść</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation>Domyślne</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>Ustawienia yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Ogólne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>UI</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Lista Gier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>System</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Profile</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>System plików</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Sterowanie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Skróty klawiszowe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation>CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Wyszukiwanie usterek</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Grafika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation>Zaawansowane</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Dźwięk</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Usługi</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>Karta SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Gamecard</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Ścieżka</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Obecna Gra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Ogólne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation>Emulacja CPU Wielordzeniowa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Potwierdź wyjście podczas emulacji</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation>Ustawienia API</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation>API:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation>Urządzenie:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation>Ustawienia Graficzne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation>Format obrazu:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation>Domyślne (16:9)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation>Wymuś 4:3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation>Wymuś 21:9</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation>Rozciągnij do Okna</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation>Ustaw kolor tła:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Kolor tła</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation>Urządzenie graficzne OpenGL</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation>Domyślne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation>2x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation>4x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation>8x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation>16x</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Ustawienia Skrótów Klawiszowych</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation>Wyczyść wszystko</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation>Przywróć domyślne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Akcja</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Skrót klawiszowy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Kontekst</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation>Przywróć ustawienia domyślne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation>Wyczyść</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Gracz 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Gracz 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Gracz 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Gracz 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Gracz 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Gracz 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Gracz 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Gracz 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Zaawansowane</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation>Tryb Konsoli</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation>Zadokowany</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation>Niezadokowany</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation>Wibracje</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation>Ruch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Ustaw</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation>Kontrolery</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation>2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation>3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation>4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation>5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation>6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation>7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation>8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation>Połączone</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation>Domyślne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation>Wyczyść</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Ustawienie wejścia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation>Kolory Joyconów</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation>Gracz 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation>Gracz 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation>Gracz 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation>Gracz 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation>Gracz 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation>Gracz 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation>Gracz 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation>Gracz 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation>Inne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation>Klawiatura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation>Zaawansowane</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation>Ekran dotykowy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation>Mysz</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation>Ruch / Dotyk</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation>Konfiguruj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation>Debuguj kontroler</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Ustawienie wejścia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation>Połacz Kontroler</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation>Pro Controller</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation>Para Joyconów</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation>Lewy Joycon</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation>Prawy Joycon</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation>Handheld</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation>Urządzenie Wejściowe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation>Klawiatura/Mysz</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation>Profil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation>Zapisz</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation>Nowy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation>Usuń</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Lewa gałka</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation>Góra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation>Lewo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation>Prawo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation>Dół</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation>Naciśnięty</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation>Modyfikator</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation>Zasięg</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation>Martwa strefa: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation>Zasięg Modyfikatora: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation>D-Pad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation>ZL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation>Minus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation>Zrzut ekranu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation>Plus</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation>Home</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation>ZR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation>SL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation>SR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Przednie klawisze</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Prawa gałka</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation>Martwa strefa: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation>Zasięg Modyfikatora: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation>[oczekiwanie]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation>Konfiguruj Ruch / Dotyk</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation>Ruch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation>Czułość:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation>Dotyk</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation>Kalibracja:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation>(100, 50) - (1800, 850)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation>Konfiguruj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation>Serwer:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation>Port:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation>Pad:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation>Dowiedz się więcej</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation>Test</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation>Myszka (Prawy Przycisk)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation>Testowanie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation>Konfigurowanie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation>Test Udany</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation>Citra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Ustaw mysz</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Przyciski myszy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Do przodu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Do tyłu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Lewy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Środkowy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Prawy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Wyczyść</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation>Domyślne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[nie ustawione]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Przywróć ustawienie domyślne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[naciśnij przycisk]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation>Informacje</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation>Nazwa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation>Identyfikator gry</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation>Nazwa pliku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation>Format</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation>Wersja</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation>Rozmiar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation>Deweloper</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation>Dodatki</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation>Ogólne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation>System</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation>Grafika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation>Dźwięk</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation>Właściwości</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation>Wersja</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Menedżer Profili</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Obecny użytkownik</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Nazwa Użytkownika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Ustaw zdjęcie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Dodaj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Zmień nazwę</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Usuń</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>Menedżer Profili nie jest dostępny gdy gra jest uruchomiona.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Wpisz nazwę użytkownika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Użytkownicy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Wprowadź nazwę dla nowego użytkownika:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Wpisz nową nazwę użytkownika:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Potwierdź usunięcie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Zamierzasz usunąć użytkownika &quot;%1&quot;. Jesteś pewien?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Ustaw zdjęcie użytkownika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>Obrazki JPEG (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Bład usunięcia zdjęcia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Błąd podczas próby nadpisania poprzedniego zdjęcia dla: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Błąd usunięcia pliku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Nie można usunąć istniejącego pliku: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Błąd podczas tworzenia folderu ze zdjęciem użytkownika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Nie można utworzyć ścieżki %1 do przechowywania zdjęć użytkownika.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Błąd kopiowania zdjęcia użytkownika</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Nie można skopiować zdjęcia z %1 do %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Ustawienia systemu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation>Region:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation>Automatyczny</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation>Domyślne</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation>CET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation>CST6CDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation>Cuba</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation>EET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation>Egipt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation>Irlandia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation>EST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation>EST5EDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation>GMT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation>GMT+0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation>GMT-0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation>GMT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation>Greenwich</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation>Hongkong</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation>HST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation>Islandia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation>Iran</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation>Izrael</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation>Jamajka</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation>Japonia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation>Kwajalein</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation>Libia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation>MET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation>MST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation>MST7MDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation>Navajo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation>NZ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation>NZ-CHAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation>Polska</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation>Portugalia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation>PRC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation>PST8PDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation>ROC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation>ROK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Uwaga: można to zmienić, gdy ustawienie regionu jest wybierane automatycznie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Japoński (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Angielski (English)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Francuski (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Niemiecki (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Włoski (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Hiszpański (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Chiński</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Koreański (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Duński (Holandia)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Portugalski (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Rosyjski (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Tajwański</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Angielski (Brytyjski)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Fancuski (Kanada)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Hiszpański (Latin American)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Chiński (Uproszczony)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Chiński tradycyjny (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Język</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>Ziarno RNG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Stereo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Surround</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>Indentyfikator konsoli:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Tryb wyjścia dźwięku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Wygeneruj ponownie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>Ustawienia systemu są dostępne tylko wtedy, gdy gra nie jest uruchomiona.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>To zamieni twojego obecnego Switch&apos;a z nowym. Twojego obecnego Switch&apos;a nie będzie można przywrócić. To może wywołać nieoczekiwane problemy w grach. To może nie zadziałać, jeśli używasz nieaktualnej konfiguracji zapisu gry. Kontynuować?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Ostrzeżenie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>Identyfikator konsoli: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Skonfiguj ekran dotykowy</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Ostrzeżenie: Ustawienia na tej stronie mają wpływ na działanie emulowanego ekranu dotykowego Yuzu. ch zmiana może spowodować niepożądane zachowanie, takie jak częściowo lub całkowicie nie działający ekran dotykowy. Powinieneś/naś używać tej strony tylko wtedy, gdy wiesz, co robisz.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Parametry dotyku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Średnica dotyku Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Palec</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Średnica dotyku X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Kąt rotacji</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Przywróć domyślne</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Lista Gier</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Angielski</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Forma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>Usługa internetowa yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Podając swoją nazwę użytkownika i token, zgadzasz się na umożliwienie yuzu zbierania dodatkowych danych o użytkowaniu, które mogą zawierać informacje umożliwiające identyfikację użytkownika.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Zweryfikuj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Zaloguj się</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Nazwa użytkownika:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>Czym jest mój token?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Udostępniaj anonimowe dane o użytkowaniu zespołowi yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Dowiedz się więcej</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>Identyfikator telemetrii:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Wygeneruj ponownie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Obecność na discordzie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Pokazuj obecną grę w twoim statusie Discorda</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Dowiedz się więcej&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Zaloguj się&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Czym jest mój token?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>Identyfikator telemetrii: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Weryfikowanie...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Weryfikowanie nieudane</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Dane anonimowe są gromadzone&lt;/a&gt; aby ulepszyć yuzu. &lt;br/&gt;&lt;br/&gt;Czy chcesz udostępnić nam swoje dane o użytkowaniu?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Sprawdzanie tekstu nie powiodło się</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Aktualna prędkość emulacji. Wartości większe lub niższe niż 100% wskazują, że emulacja działa szybciej lub wolniej niż Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Ile klatek na sekundę gra aktualnie wyświetla. To będzie się różnić w zależności od gry, od sceny do sceny.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Czas potrzebny do emulacji klatki na sekundę Switcha, nie licząc ograniczania klatek ani v-sync. Dla emulacji pełnej szybkości powinno to wynosić co najwyżej 16,67 ms.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Usuń &quot;Ostatnie pliki&quot;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>OSTRZEŻENIE! Nieaktualny format gry</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Używasz zdekonstruowanego formatu katalogu ROM dla tej gry, który jest przestarzałym formatem, który został zastąpiony przez inne, takie jak NCA, NAX, XCI lub NSP. W zdekonstruowanych katalogach ROM brakuje ikon, metadanych i obsługi aktualizacji.&lt;br&gt;&lt;br&gt; Aby znaleźć wyjaśnienie różnych formatów Switch obsługiwanych przez yuzu,&lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt; sprawdź nasze wiki&lt;/a&gt;. Ta wiadomość nie pojawi się ponownie.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>Błąd podczas wczytywania ROMu!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>Ten format ROMu nie jest wspierany.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Wystąpił błąd podczas inicjowania rdzenia wideo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu napotkał błąd podczas działania rdzenia wideo, proszę zobaczyć log po więcej szczegółów. Aby uzyskać więcej informacji na temat uzyskiwania dostępu do pliku log, zobacz następującą stronę: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Jak przesłać plik log? &lt;/a&gt;Upewnij się, że masz najnowsze sterowniki karty graficznej.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Wystąpił nieznany błąd. Więcej informacji można znaleźć w pliku log.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Start</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Błąd podczas otwarcia folderu %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>Folder nie istnieje!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>Wypakowanie RomFS nieudane!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Wystąpił błąd podczas kopiowania plików RomFS lub użytkownik anulował operację.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Wybierz tryb zrzutu RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Proszę wybrać w jaki sposób chcesz, aby zrzut pliku RomFS został wykonany. &lt;br&gt;Pełna kopia ze wszystkimi plikami do nowego folderu, gdy &lt;br&gt;skielet utworzy tylko strukturę folderu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>Wypakowywanie RomFS...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Anuluj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>Wypakowanie RomFS zakończone pomyślnie!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>Operacja zakończona sukcesem.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Wybierz folder...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Właściwości</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>Właściwości tej gry nie mogły zostać załadowane.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Plik wykonywalny Switcha (%1);;Wszystkie pliki (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Załaduj plik...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Otwórz folder wypakowanego ROMu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Wybrano niewłaściwy folder</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>Folder wybrany przez ciebie nie zawiera &apos;głownego&apos; pliku.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Instalowanie pliku &quot;%1&quot;...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Aplikacja systemowa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Archiwum systemu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Aktualizacja aplikacji systemowej</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Paczka systemowa (Typ A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Paczka systemowa (Typ B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Gra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Aktualizacja gry</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>Dodatek do gry</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Tytuł Delta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Wybierz typ instalacji NCA...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Wybierz typ tytułu, do którego chcesz zainstalować ten NCA, jako:
+(W większości przypadków domyślna &quot;gra&quot; jest w porządku.)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Instalacja nieudana</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>Typ tytułu wybrany dla NCA jest nieprawidłowy.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Nie znaleziono pliku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Nie znaleziono pliku &quot;%1&quot;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Kontynuuj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Brakuje konta Yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Aby przesłać test zgodności gry, musisz połączyć swoje konto yuzu.&lt;br&gt;&lt;br/&gt; Aby połączyć swoje konto yuzu, przejdź do opcji Emulacja &amp;gt; Konfiguracja &amp;gt; Sieć.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Plik Amiibo (%1);;Wszyskie pliki (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Załaduj Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Błąd otwarcia pliku danych Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Nie można otworzyć pliku Amiibo &quot;%1&quot; do odczytu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Błąd podczas odczytu pliku danych Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Nie można w pełni odczytać danych Amiibo. Oczekiwano odczytu %1 bajtów, ale był on w stanie odczytać tylko %2 bajty.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Błąd podczas ładowania pliku danych Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Nie można załadować danych Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Zrób zrzut ekranu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>Obrazek PNG (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Prędkość: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Prędkość: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Gra: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Klatka: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>Gra, którą próbujesz wczytać, wymaga dodatkowych plików z Switch&apos;a, które zostaną zrzucone przed graniem.&lt;br/&gt;&lt;br/&gt; Aby uzyskać więcej informacji na temat wyrzucania tych plików, odwiedź następującą stronę wiki:&lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt; Zrzut archiw systemu i udostępnionych czcionek z konsoli Nintendo Switch&lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;Czy chcesz wrócić do listy gier? Kontynuacja emulacji może spowodować awarie, uszkodzone dane zapisu lub inne błędy.
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Archiwum systemu nie znalezione.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Czcionki nie zostały znalezione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Fatalny błąd</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu napotkał błąd, proszę zobaczyć log po więcej szczegółów. Aby uzyskać więcej informacji na temat uzyskiwania dostępu do pliku log, zobacz następującą stronę: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Jak przesłać plik log&lt;/a&gt;?&lt;br/&gt;&lt;br/&gt; Czy chcesz wrócić do listy gier? Kontynuacja emulacji może spowodować awarie, uszkodzone dane zapisu lub inne błędy.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Potwierdź ponowną aktywacje klucza</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Zamierzasz zmusić wszystkie swoje klucze do ponownej aktywacji.
+Jeśli nie wiesz, co to oznacza i co robisz,
+jest to potencjalnie destrukcyjne działanie.
+Upewnij się, że to jest to, czego chcesz
+i opcjonalnie tworzyć kopie zapasowe.
+
+Spowoduje to usunięcie wygenerowanych automatycznie plików kluczy i ponowne uruchomienie modułu pochodnego klucza.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Wyprowadzanie kluczy...
+Zależnie od tego może potrwać do minuty
+na wydajność twojego systemu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Wyprowadzanie kluczy...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Wybierz cel zrzutu RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Proszę wybrać RomFS, jakie chcesz zrzucić.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Czy na pewno chcesz zamknąć yuzu?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Czy na pewno chcesz zatrzymać emulację? Wszystkie niezapisane postępy zostaną utracone.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Nazwa gry</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Kompatybilność</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Dodatki</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Typ pliku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Rozmiar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Otwórz lokalizację zapisów</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Otwórz lokalizację modyfikacji</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Zrzuć RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Kopiuj identyfikator gry do schowka</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Nawiguj do wpisu kompatybilności gry</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Właściwości</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Perfekcyjnie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>Funkcje gry są bezbłędne, bez żadnych zakłóceń audio i graficznych, wszystkie przetestowane funkcje działają zgodnie z przeznaczeniem bez
+wszelkich potrzebnych obejść.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Świetnie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>Funkcje gry z drobnymi usterkami graficznymi lub dźwiękowymi i można je odtwarzać od początku do końca. Może wymagać niektórych
+obejść.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>W porządku</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>Funkcje gry z dużymi usterkami graficznymi lub dźwiękowymi, ale gra jest odtwarzana od początku do końca z użyciem
+obejść.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Zła</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>Funkcje gry, ale z dużymi usterkami graficznymi lub dźwiękowymi. Nie można wykonać postępu w określonych obszarach z powodu problemów
+nawet z obejściami.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>Gra jest całkowicie niemożliwa do zagrania z powodu poważnych usterków graficznych lub dźwiękowych. Nie można przejść ekran
+startowy.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Nie uruchamia się</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>Ta gra się zawiesza przy próbie startu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Nie testowane</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>Ta gra nie została jeszcze przetestowana.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Kliknij podwójnie aby dodać folder do listy gier</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filter:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Wpisz typ do filtra</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Uruchamianie...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Plik</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Ostatnie pliki</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Emulacja</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Widok</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Debugowanie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Narzędzia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Pomoc</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Załaduj plik...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Załaduj folder...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>&amp;Wyjście</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Start</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pauza</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Stop</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Reinicjalizuj klucze...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>O yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Tryb jednego okna</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Konfiguruj...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Wyświetlaj nagłówki Dock Widget</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Pokaż pasek fitrowania</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Pokaż pasek statusu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Pełny ekran</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Zrestartuj</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Załaduj Amiibo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Zgłoś kompatybilność</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Otwórz folder yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Zrób zrzut ekranu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroProfile</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[nie ustawione]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Krzyżak %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Oś %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Przycisk %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[nieznane]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[nieużywane]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Oś %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Wybierz użytkownika:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Wybór profilu</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Wpisz tekst:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Klawiatura systemowa</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Stos wywołań</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>czekam na mutex 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>ma oczekujących: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>uchwyt właściciela: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>czekam na wszystkie objekty</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>oczekiwanie na jeden z następujących obiektów</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>Uruchomione</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>Gotowe</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>Spauzowana</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>czekam na powrót HLE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>spanie</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>czekam na odpowiedź IPC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>oczekiwanie na obiekty</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>czekam na mutex</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>czekam na arbitra adresu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>drzemiący</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>śmierć</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation> PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>rdzeń %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Nieznany procesor %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>procesor = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>idealny rdzeń = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>maska powinowactwa = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>identyfikator wątku = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>piorytet = %1(obecny) / %2(normalny)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>ostatnie działające kleszcze = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>nie czekam na mutex</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>czekanie na wątek</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Drzewo czekania</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/pt_BR.ts b/dist/languages/pt_BR.ts
new file mode 100644
index 000000000..c842793f4
--- /dev/null
+++ b/dist/languages/pt_BR.ts
@@ -0,0 +1,4757 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="pt_BR" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>Sobre o yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu é um emulador experimental de código aberto para o Nintendo Switch licenciado sob a GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;Esse programa não deve ser utilizado para jogar jogos que você não obteve legalmente.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Site&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Código fonte&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contribuidores&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Licença&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; é uma marca comercial da Nintendo. O yuzu não é afiliado com a Nintendo de nenhuma forma.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>Comunicando com o servidor...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>Cancelar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation>Toque no canto superior esquerdo &lt;br&gt;do seu touchpad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation>Agora toque no canto inferior direito &lt;br&gt;do seu touchpad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>Configuração concluída!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>OK</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Informar compatibilidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Informar compatibilidade de jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Ao enviar um caso de teste à &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;lista de compatibilidade do yuzu&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, as seguintes informações serão recolhidas e exibidas no site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Informações de hardware (CPU / GPU / sistema operacional)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Qual versão do yuzu você está usando&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;A conta do yuzu conectada&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Perfeito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo funciona impecavelmente, sem falhas no áudio ou nos gráficos.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Ótimo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo funciona com leves falhas gráficas ou de áudio e é jogável do início ao fim. Pode exigir algumas soluções alternativas.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>Razoável</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo funciona com grandes falhas gráficas ou de áudio, mas é jogável do início ao fim com o uso de soluções alternativas.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Ruim</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo funciona, só que com problemas significativos nos gráficos ou no áudio. Não é possível progredir em áreas específicas por conta de tais problemas, mesmo com soluções alternativas.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;É impossível jogar o jogo devido a grandes problemas nos gráficos ou no áudio. Não é possível passar da tela inicial do jogo.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Não inicia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo trava ou se encerra abruptamente ao se tentar iniciá-lo.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Sem considerar velocidade e desempenho, quão bem o jogo se comporta, do início ao fim, nesta versão do yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Agradecemos pelo seu relatório!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Enviando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Erro de comunicação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Um erro ocorreu ao enviar o caso de teste</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Próximo</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Áudio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Mecanismo de saída:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Este efeito de pós-processamento ajusta a velocidade do áudio para acompanhar a velocidade de emulação e ajuda a evitar cortes no áudio. No entanto, isto aumenta a latência do áudio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Ativar alongamento de áudio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Dispositivo de áudio:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation>Usar volume global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>Definir volume:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Volume:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation>Precisão:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation>Preciso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>Não seguro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>Ativar modo de depuração</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation>Recomendamos definir a precisão para &quot;Preciso&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation>Ajustes de otimização não seguros de CPU </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation>Estes ajustes reduzem a precisão para aprimorar a velocidade.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation>Não usar FMA (melhora o desempenho em CPUs sem FMA)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta opção melhora o desempenho reduzindo a precisão de instruções de multiplicação-adição unificada (FMA) em CPUs sem suporte nativo a este recurso.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation>FRSQRTE e FRECPE mais rápidos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta opção melhora o desempenho de algumas instruções de ponto flutuante usando aproximações nativas menos precisas.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Os ajustes de CPU só estão disponíveis enquanto o jogo não estiver em execução.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation>Ativando o modo de depuração de CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation>O modo de depuração de CPU é intencionado para uso por desenvolvedores. Deseja mesmo ativá-lo?</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation>Ative ou desative otimizações de CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;
+ &lt;b&gt;Apenas para depuração.&lt;/b&gt;
+ &lt;br&gt;
+ Se você não tem certeza do que estas opções fazem, mantenha-as todas ativadas.
+ &lt;br&gt;
+ Estes ajustes apenas têm efeito quando a precisão da CPU for definida como &quot;Ativar modo de depuração&quot;.
+ &lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation>Ativar tabelas de página em linha</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Esta otimização acelera acessos de memória pelo programa convidado.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Quando ativada, permite o acesso inline a PageTable::pointers no código emitido.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Quando desativada, força a passagem de todos os acessos de memória pelas funções Memory::Read/Memory::Write.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation>Ativar vinculação de blocos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta otimização evita buscas do dispatcher ao permitir que blocos básicos emitidos pulem diretamente para outros blocos básicos se o PC de destino for estático.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation>Ativar buffer de pilha de retorno</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Esta otimização evita buscas do dispatcher ao monitorar possíveis endereços de retorno de instruções BL. Isto se aproxima do que ocorre em um buffer de pilha de retorno em um CPU real.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation>Ativar dispatcher rápido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Ativa um sistema de dispatch de dois níveis. Um dispatcher mais rápido, escrito em assembly e que possui um pequeno cache MRU de destinos de pulo, é usado primeiro. Caso falhe, o dispatcher mais lento escrito em C++ será usado em seu lugar.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation>Ativar eliminação de contexto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Ativa uma otimização da IR que reduz acessos desnecessários à estrutura de contexto da CPU.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation>Ativar propagação de constantes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Ativa otimizações da IR que envolvem propagação de constantes.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation>Ativar otimizações diversas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div&gt;Ativa otimizações diversas para a IR.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation>Ativar redução de checagem de desalinhamento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Quando ativada, um desalinhamento só será disparado quando um acesso cruza o limite de uma página.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Quando desativada, um desalinhamento será disparado em todos os acessos desalinhados.&lt;/div&gt;
+ </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>Os ajustes de CPU estão disponíveis apenas quando não houver nenhum jogo em execução.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>Ativar GDB stub</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Porta:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Registros de depuração</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Filtro global de registros</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Mostrar console de registros (apenas Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Abrir local dos registros</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Linha de argumentos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation>Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation>Quando ativado, a API gráfica entra em um modo de depuração mais lento.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation>Ativar depuração de gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation>Quando ativado, desativa o macro compilador Just in Time. Ativar isto faz os jogos rodarem mais lentamente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation>Desativar macro JIT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation>Extrair</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Ativar serviços de relatório detalhado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Isto será restaurado automaticamente assim que o yuzu for fechado.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Avançado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Modo quiosque (Quest)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation>Configurar controle de depuração</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation>Limpar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation>Padrão</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>Configurações do yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>Interface</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Lista de jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Perfis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Sistema de arquivos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Controles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Teclas de atalho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation>CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Depuração</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation>Avançado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation>GráficosAvançado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Áudio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Rede</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Serviços</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Pastas de armazenamento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>Cartão SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Cartão de jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Caminho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Inserido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Jogo atual</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Gerenciador de patches</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Extrair NSOs descompactados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>Extrair ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Raiz de carregamento de mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>Extrair raiz</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Ajustes de cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Diretório do cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>Metadados da lista de jogos em cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Restaurar cache de metadados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Selecione a pasta da NAND emulada...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Selecione a pasta do SD emulado...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>Selecione o local do Gamecard...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Selecione a pasta de extração...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Selecione a pasta de carregamento de mods...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Selecione a pasta de cache...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>O cache de metadados já está vazio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>A operação foi concluída com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>O cache de metadados não pôde ser excluído. Ele pode estar em uso no momento ou não existe.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Limitar percentual de velocidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation>Emulação de CPU multinúcleo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Confirmar saída quando a emulação estiver em execução</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>Escolher um usuário ao iniciar um jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Pausar emulação quando a janela ficar em segundo plano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation>Esconder cursor do mouse quando em inatividade</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation>Configurações de API</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation>API:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation>Dispositivo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation>Configurações gráficas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Usar cache de shaders em disco</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Usar emulação assíncrona da GPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation>Proporção de tela:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation>Padrão (16:9)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation>Forçar 4:3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation>Forçar 21:9</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation>Esticar para a janela</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation>Usar cor de fundo global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation>Configurar cor de fundo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Cor de fundo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation>Dispositivo gráfico do OpenGL</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation>Configurações gráficas avançadas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation>Nível de precisão:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation>A sincronização vertical (VSync) evita que as imagens do jogo pareçam cortadas, porém algumas placas gráficas apresentam redução de desempenho quando estiver ativa. Deixe-a ativada se você não reparar alguma diferença de desempenho.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation>Usar sincronização vertical (apenas OpenGL)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation>Ativar isto reduz engasgos de shaders. Ativa os shaders assembly do OpenGL em dispositivos compatíveis da Nvidia (é necessária a extensão NV_gpu_program5). Esta é uma opção experimental.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation>Usar shaders assembly (experimental, apenas OpenGL + Nvidia)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation>Realiza a compilação de shaders de forma assíncrona, o que pode reduzir engasgos de shaders. Esta opção é experimental.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation>Usar compilação assíncrona de shaders (experimental)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation>Usar tempo de resposta rápido da GPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation>Filtragem anisotrópica:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation>Padrão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation>2x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation>4x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation>8x</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation>16x</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Configurações de teclas de atalho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>Clique duas vezes em um atalho para alterá-lo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation>Limpar tudo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation>Restaurar padrões</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Ação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Atalho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Contexto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Combinação de teclas já utilizada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation>A sequência de teclas pressionada já esta atribuída para: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation>Restaurar padrão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation>Limpar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation>A sequência de teclas padrão já esta atribuida para: %1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation>ConfigurarEntrada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Jogador 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Jogador 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Jogador 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Jogador 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Jogador 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Jogador 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Jogador 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Jogador 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Avançado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation>Modo do console</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation>Na base</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation>Fora da base</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation>Vibração</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation>Movimento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Configurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation>Controles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation>2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation>3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation>4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation>5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation>6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation>7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation>8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation>Conectado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation>Padrões</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation>Limpar</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configurar entrada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation>Cores dos Joycon</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation>Jogador 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation>Joycon esq.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation>Botão L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation>Joycon dir.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation>Botão R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation>Jogador 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation>Jogador 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation>Jogador 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation>Jogador 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation>Jogador 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation>Jogador 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation>Jogador 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation>Outro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation>Teclado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation>Avançado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation>Touchscreen</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation>Mouse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation>Movimento/toque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation>Configurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation>Controle de depuração</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configurar controles</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation>Conectar controle</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation>Pro Controller</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation>Par de Joycons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation>Joycon Esquerdo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation>Joycon Direito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation>Portátil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation>Dispositivo de entrada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation>Qualquer</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation>Teclado/mouse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation>Perfil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation>Salvar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation>Novo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation>Excluir</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Analógico esquerdo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation>Cima</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation>Esquerda</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation>Direita</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation>Baixo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation>Pressionado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation>Modificador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation>Alcance</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation>Zona morta: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation>Alcance de modificador: 0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation>D-pad</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation>ZL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation>Menos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation>Capturar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation>Mais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation>Botão Home</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation>ZR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation>SL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation>SR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Botões de rosto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Analógico direito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation>Zona morta: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation>Alcance de modificador: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation>[esperando]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation>Configurar movimento/toque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation>Movimento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation>Fonte dos dados de movimento:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation>Sensibilidade:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation>Toque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation>Fonte dos dados de toque:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation>Calibração:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation>(100, 50) - (1800, 850)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation>Configurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation>Usar mapeamento de botões:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation>Configuração CemuhookUDP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation>Você pode utilizar qualquer dispositivo de entrada compatível com o Cemuhook UDP para prover dados de movimento e toque.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation>Servidor:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation>Porta:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation>Direcionais:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation>Direcional 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation>Direcional 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation>Direcional 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation>Direcional 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation>Saiba mais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation>Teste</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation>Mouse (botão direito)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation>CemuhookUDP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation>Janela do emulador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Saiba mais&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation>Testando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation>Configurando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation>Teste bem-sucedido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation> Dados foram recebidos do servidor com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation>O teste falhou</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation>Não foi possível receber dados válidos do servidor.&lt;br&gt;Verifique se o servidor foi configurado corretamente e o endereço e porta estão corretos.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation>Citra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation>Um teste UDP ou configuração de calibração está em curso no momento.&lt;br&gt;Aguarde até a sua conclusão.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Configurar mouse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Botões do mouse</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Dianteiro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Traseiro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Esquerdo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Meio:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Direito:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Limpar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation>Padrões</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[não definido]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Restaurar padrão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[pressione uma tecla]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation>Diálogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation>Informações</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation>Nome</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation>ID do título</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation>Nome do arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation>Versão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation>Tamanho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation>Desenvolvedor</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation>Adicionais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation>Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation>Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation>Gráficos avanç.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation>Áudio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation>Propriedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation>Usar configuração global (%1)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation>Nome do patch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation>Versão</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Gerenciador de perfis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Usuário atual</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Nome de usuário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Definir imagem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Adicionar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Renomear</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Excluir</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>Esta tela só fica disponível apenas quando não houver nenhum jogo em execução.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Escreva o nome de usuário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Usuários</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Digite o nome do novo usuário:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Digite um novo nome de usuário:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Confirmar exclusão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Você está prestes a excluir o usuário &quot;%1&quot;. Tem certeza?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Selecione a imagem do usuário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>Imagens JPEG (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Erro ao excluir a imagem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Ocorreu um erro ao tentar substituir a imagem anterior em: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Erro ao excluir arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Não foi possível excluir o arquivo existente: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Erro ao criar a pasta de imagens do usuário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Não foi possível criar a pasta %1 para armazenar as imagens do usuário.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Erro ao copiar a imagem do usuário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Não foi possível copiar a imagem de %1 para %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>BCAT é a maneira que a Nintendo usa para enviar dados aos jogos para engajar sua comunidade e desbloquear conteúdo adicional.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>Backend do BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Saiba mais sobre o BCAT, Boxcat e eventos atuais&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>O serviço Boxcat está offline ou você não está conectado à internet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Ocorreu um erro ao processar os dados de eventos do Boxcat. Entre em contato com os desenvolvedores do yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>A versão do yuzu que você está usando é ou recente demais ou antiga demais para o servidor. Tente atualizar para a versão oficial mais recente do yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>Não há eventos no Boxcat atualmente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation>Eventos Boxcat atuais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>O yuzu está baixando o status mais recente do Boxcat...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Configurações de sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation>Região:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation>Automático</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation>Padrão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation>CET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation>CST6CDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation>Cuba</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation>EET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation>Egito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation>Eire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation>EST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation>EST5EDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation>GB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation>GB-Eire</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation>GMT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation>GMT+0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation>GMT-0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation>GMT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation>Greenwich</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation>Hongkong</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation>HST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation>Islândia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation>Irã</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation>Israel</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation>Jamaica</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation>Japão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation>Ilhas Marshall</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation>Líbia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation>MET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation>MST</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation>MST7MDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation>Navajo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation>NZ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation>NZ-CHAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation>Polônia </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation>Portugal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation>PRC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation>PST8PDT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation>ROC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation>ROK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation>Singapura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation>Turquia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation>UCT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation>Universal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation>UTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation>W-SU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation>WET</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation>Zulu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation>EUA</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation>Europa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation>Austrália</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation>China</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation>Coréia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation>Taiwan</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation>Fuso horário:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Nota: isso pode ser substituído caso a configuração de região automática esteja ativada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Japônes (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Inglês (English)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Francês (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Alemão (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Italiano (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Espanhol (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Chinês</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Coreano (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Holandês (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Português</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Russo (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Taiwanês</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Inglês britânico (British English)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Francês canadense (Canadian French)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Espanhol latino-americano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Chinês simplificado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Chinês tradicional (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>Data e hora personalizada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Idioma</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>Semente RNG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Estéreo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Surround</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>ID do console:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Modo de saída de som</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM yyyy h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Regerar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>As configurações de sistema são acessíveis apenas quando não houver nenhum jogo em execução.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Isto substituirá o seu Switch virtual atual por um novo. O seu Switch virtual atual não poderá ser recuperado. Isto pode causar efeitos inesperados em jogos. Isto pode falhar caso você use um jogo salvo com configurações desatualizadas registradas nele. Continuar?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Aviso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>ID do console: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation>Configurar mapeamento de toque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation>Mapeamento:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation>Novo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation>Excluir</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation>Renomear</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation>Clique na área inferior para adicionar um ponto, e então pressione um botão para mapear.
+Mova os pontos para mudar a posição, ou clique duas vezes nas células da tabela para editar os valores.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation>Excluir ponto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation>Botão</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation>Novo perfil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation>Insira o nome do novo perfil.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation>Excluir perfil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation>Excluir perfil %1?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation>Renomear perfil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation>Novo nome:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation>[pressione a tecla]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Configurar tela de toque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Aviso: Os ajustes nesta página afetam o funcionamento interno da tela de toque emulada do yuzu. Alterá-los pode resultar em comportamentos indesejáveis, tais como a tela de toque funcionar parcialmente ou não responder por completo. Apenas faça alterações caso você saiba o que está fazendo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Parâmetros de toque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Diâmetro de toque Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Dedo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Diâmetro de toque X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Ângulo rotacional</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Restaurar padrões</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Nota: alterar o idioma aplicará as suas configurações.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Idioma da interface:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Tema:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Lista de jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Mostrar coluna de adicionais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Tamanho do ícone:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Texto da 1ª linha:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Texto da 2ª linha:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation>Capturas de tela</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation>Perguntar onde salvar capturas de tela (apenas Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation>Pasta para capturas de tela:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation>Selecione a pasta de capturas de tela...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Inglês</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formulário</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>yuzu Web Service</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Ao informar seu usuário e token, você concorda em permitir ao yuzu recolher dados de uso adicionais, que podem incluir informações de identificação de usuário.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Verificar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Cadastrar-se</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Nome de usuário:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>Qual é o meu token?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Compartilhar anonimamente dados de uso com a equipe do yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Saiba mais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>ID de telemetria:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Gerar um novo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Presença no Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Mostrar o jogo atual no seu status do Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Saiba mais&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Cadastrar-se&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Qual é o meu token?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>ID de telemetria: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Não especificado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>Token não verificado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>O token não foi verificado. A alteração no seu token não foi salva.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Verificando...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Falha na verificação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Falha na verificação. Verifique se o seu token foi inserido corretamente e se a sua conexão à internet está funcionando.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Dados anônimos são recolhidos&lt;/a&gt; para ajudar a melhorar o yuzu. &lt;br/&gt;&lt;br/&gt;Gostaria de compartilhar os seus dados de uso conosco?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Falha na verificação de texto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>Carregando applet web...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Sair do applet web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Sair</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Para sair do aplicativo web, use os controles fornecidos pelo jogo para sair dele, selecione a opção &quot;Sair do applet web&quot; na barra de menu ou pressione a tecla &quot;Enter&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Applet web</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Esta versão do yuzu foi compilada sem suporte a QtWebEngine, o que significa que o yuzu não pode exibir adequadamente o manual do jogo ou a página da web solicitada.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation>A quantidade de shaders sendo construídos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Velocidade atual de emulação. Valores maiores ou menores que 100% indicam que a emulação está rodando mais rápida ou lentamente que em um Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Quantos quadros por segundo o jogo está exibindo atualmente. Isto irá variar de jogo para jogo e cena para cena.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Tempo que leva para emular um quadro do Switch, sem considerar o limitador de taxa de quadros ou a sincronização vertical. Um valor menor ou igual a 16.67 ms indica que a emulação está em velocidade plena.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation>NA BASE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation>ASYNC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation>MULTINÚCLEO</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation>VULKAN</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation>OPENGL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Limpar arquivos recentes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Aviso - formato de jogo desatualizado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Você está usando neste jogo o formato de ROM desconstruída e extraída em uma pasta, que é um formato desatualizado que foi substituído por outros, como NCA, NAX, XCI ou NSP. Pastas desconstruídas de ROMs não possuem ícones, metadados e suporte a atualizações.&lt;br&gt;&lt;br&gt;Para saber mais sobre os vários formatos de ROMs de Switch compatíveis com o yuzu, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;confira a nossa wiki&lt;/a&gt;. Esta mensagem não será exibida novamente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>Erro ao carregar a ROM!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>O formato da ROM não é suportado.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Ocorreu um erro ao inicializar o núcleo de vídeo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>O yuzu encontrou um erro ao executar o núcleo de vídeo. Consulte o registro para mais detalhes. Para mais informações em como acessar o registro, veja a seguinte página: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Como enviar o arquivo de registro&lt;/a&gt;. Verifique se você está usando o driver de vídeo mais atualizado.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation>Erro ao carregar a ROM!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Ocorreu um erro desconhecido. Consulte o registro para mais detalhes.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Iniciar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Dados de jogos salvos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Dados de mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Erro ao abrir a pasta %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>A pasta não existe!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Erro ao abrir o cache de shaders transferível</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>Não existe um cache de shaders para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation>Conteúdo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation>Atualização</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation>DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation>Remover item</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation>Remover o jogo instalado %1?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation>Removido com sucesso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation>O jogo base foi removido com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation>Erro ao remover %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation>O jogo base não está instalado na NAND e não pode ser removido.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation>A atualização instalada foi removida com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation>Não há nenhuma atualização instalada para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation>Não há nenhum DLC instalado para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation>%1 DLC(s) instalados foram removidos com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation>Excluir o cache de shaders transferível?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation>Remover configurações customizadas do jogo?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation>Remover arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation>Erro ao remover cache de shaders transferível</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation>O cache de shaders transferível foi removido com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation>Falha ao remover o cache de shaders transferível.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation>Erro ao remover as configurações customizadas do jogo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation>Não há uma configuração customizada para este título.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation>As configurações customizadas do jogo foram removidas com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation>Falha ao remover as configurações customizadas do jogo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>Falha ao extrair RomFS!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Houve um erro ao copiar os arquivos RomFS ou o usuário cancelou a operação.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Extração completa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>Apenas estrutura</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Selecione o modo de extração do RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Selecione a forma como você gostaria que o RomFS seja extraído.&lt;br&gt;&quot;Extração completa&quot; copiará todos os arquivos para a nova pasta, enquanto que &lt;br&gt;&quot;Apenas estrutura&quot; criará apenas a estrutura de pastas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>Extraindo RomFS...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Cancelar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>Extração do RomFS concluida!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>A operação foi concluída com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Erro ao abrir %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Selecionar pasta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Propriedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>As propriedades do jogo não puderam ser carregadas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Executável do Switch (%1);;Todos os arquivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Carregar arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Abrir pasta da ROM extraída</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Pasta inválida selecionada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>A pasta que você selecionou não contém um arquivo &apos;main&apos;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation>Arquivo de Switch instalável (*.nca *.nsp *.xci);; Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation>Instalar arquivos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Instalando arquivo &quot;%1&quot;...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation>Resultados da instalação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Aplicativo do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Arquivo do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Atualização de aplicativo do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Pacote de firmware (tipo A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Pacote de firmware (tipo B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Atualização de jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>DLC de jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Título delta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Selecione o tipo de instalação do NCA...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Selecione o tipo de título como o qual você gostaria de instalar este NCA:
+(Na maioria dos casos, o padrão &apos;Jogo&apos; serve bem.)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Falha ao instalar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>O tipo de título que você selecionou para o NCA é inválido.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Arquivo não encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Arquivo &quot;%1&quot; não encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Continuar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Tela de erro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Conta do yuzu faltando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Para enviar um caso de teste de compatibilidade de jogo, você precisa entrar com a sua conta do yuzu.&lt;br&gt;&lt;br/&gt;Para isso, vá para Emulação &amp;gt; Configurar... &amp;gt; Rede.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation>Erro ao abrir URL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation>Não foi possível abrir o URL &quot;%1&quot;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Arquivo Amiibo (%1);; Todos os arquivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Carregar Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Erro ao abrir arquivo de dados do Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Não foi possível abrir o arquivo de Amiibo &quot;%1&quot; para leitura.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Erro ao ler arquivo de dados de Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Não foi possível ler completamente os dados do Amiibo. O yuzu esperava ler %1 bytes, mas foi capaz de ler apenas %2 bytes.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Erro ao carregar dados do Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Não foi possível carregar os dados do Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Capturar tela</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>Imagem PNG (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Velocidade: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Velocidade: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Jogo: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Quadro: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>O jogo que você está tentando carregar precisa que arquivos adicionais do seu Switch sejam extraídos antes de jogá-lo.&lt;br/&gt;&lt;br/&gt;Para saber mais sobre como extrair esses arquivos, visite a seguinte página da wiki: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Extraindo arquivos de sistema e fontes compartilhadas de um Switch&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt; Gostaria de voltar para a lista de jogos? Continuar com a emulação pode resultar em travamentos, dados salvos corrompidos ou outros problemas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>O yuzu não foi capaz de encontrar um arquivo de sistema do Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>O yuzu não foi capaz de encontrar um arquivo de sistema do Switch. %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Arquivo do sistema não encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Arquivo de sistema faltando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>O yuzu não foi capaz de encontrar as fontes compartilhadas do Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Fontes compartilhadas não encontradas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Fonte compartilhada faltando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Erro fatal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>O yuzu encontrou um erro fatal. Consulte o registro para mais detalhes. Para mais informações sobre como acessar o registro, consulte a seguinte página: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Como enviar o arquivo de registro&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Gostaria de voltar para a lista de jogos? Continuar com a emulação pode resultar em travamentos, dados salvos corrompidos ou outros problemas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Erro fatal encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Confirmar rederivação de chave</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Você está prestes a rederivar todas as suas chaves forçadamente.
+Se você não sabe o que isso significa ou o que você está fazendo,
+esta é uma ação potencialmente destrutiva.
+Por favor, confirme que você quer mesmo fazer isto
+e opcionalmente faça cópias de segurança.
+
+Isto excluirá o seus arquivos de chaves geradas automaticamente, e reexecutar o módulo de derivação de chaves.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation>Faltando fusíveis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation> - Faltando BOOT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation> - Faltando BCPKG2-1-Normal-Main</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation> - Faltando PRODINFO</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation>Faltando componentes de derivação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation>Há componentes faltando que podem impedir a conclusão do processo de derivação de chaves. &lt;br&gt;Por favor, siga &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;o guia de iniciação rápida&lt;/a&gt; para obter todas as suas chaves e jogos. &lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Derivando chaves...
+Isto pode demorar até um minuto, dependendo
+do desempenho do seu sistema.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Derivando chaves</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Selecionar alvo de extração do RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Selecione qual RomFS você quer extrair.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Você deseja mesmo fechar o yuzu?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Deseja mesmo parar a emulação? Qualquer progresso não salvo será perdido.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>O aplicativo rodando no momento solicitou ao yuzu para não sair.
+
+Deseja ignorar isso e sair mesmo assim?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation>OpenGL não disponível!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation>O yuzu não foi compilado com suporte para OpenGL.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation>Vulkan não disponível!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation>O yuzu não foi compilado com suporte para Vulkan.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation>Erro ao inicializar OpenGL 4.3!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation>Sua GPU pode não suportar OpenGL 4.3, ou você não tem os drivers gráficos mais recentes.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation>Erro ao inicializar o OpenGL!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation>Sua GPU pode não suportar uma ou mais extensões necessárias do OpenGL. Por favor, verifique se você possui a última versão dos drivers gráficos.&lt;br&gt;&lt;br&gt;Extensões não supportadas:&lt;br&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Nome</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Compatibilidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Adicionais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Tipo de arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Tamanho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Abrir local dos jogos salvos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Abrir local dos dados de mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Abrir cache de shaders transferível</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation>Remover</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation>Remover atualização instalada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation>Remover todos os DLCs instalados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation>Remover cache de shaders</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation>Remover configuração customizada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation>Remover todo o conteúdo instalado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Extrair RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Copiar ID do título para a área de transferência</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Abrir artigo do jogo no GameDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Propriedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Examinar subpastas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Remover pasta de jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation>▲ Mover para cima</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation>▼ Mover para baixo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Abrir local da pasta</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Perfeito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>O jogo funciona impecavelmente, sem falhas gráficas ou de áudio. Todas as funcionalidades testadas
+do jogo funcionam como deveriam, sem nenhuma solução alternativa necessária.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Ótimo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>O jogo funciona com leves falhas gráficas ou de áudio e é jogável do início ao fim. Pode exigir
+algumas soluções alternativas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Razoável</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>O jogo funciona com graves falhas gráficas ou de áudio, mas é jogável do início ao fim
+com o uso de soluções alternativas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Ruim</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>O jogo funciona, só que com problemas significativos nos gráficos ou no áudio. Não é possível progredir em áreas
+específicas por conta de tais problemas, mesmo com soluções alternativas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Intro/menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>É impossível jogar o jogo devido a grandes problemas nos gráficos ou no áudio. Não é possível passar da
+tela inicial do jogo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Não inicia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>O jogo trava ou se encerra abruptamente ao se tentar iniciá-lo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Não testado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>Esse jogo ainda não foi testado.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Clique duas vezes para adicionar uma pasta à lista de jogos</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filtro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Digite o padrão para filtrar</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation>Por favor, confirme que esses são os arquivos que deseja instalar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation>Instalar uma atualização ou DLC irá sobrescrever a instalada anteriormente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation>Instalar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation>Instalar arquivos para a NAND</translation>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>Carregando shaders 387/1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>Carregando shaders (%v de %m)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Tempo estimado 5m 4s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>Carregando...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>Carregando shaders %1/%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Iniciando...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Tempo estimado %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Arquivos recentes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Emulação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Exibir</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Depuração</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Ferramentas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Ajuda</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation>Instalar arquivos para a NAND...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Carregar arquivo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Carregar pasta...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>S&amp;air</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Iniciar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pausar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Parar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Reinicializar chaves...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>Sobre o yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Modo de janela única</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Configurar...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Exibir barra de títulos de widgets afixados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Exibir barra de filtro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Exibir barra de status</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation>Restaurar tamanho padrão de janela</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Tela cheia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Carregar Amiibo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Informar compatibilidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation>Abrir pagina de mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation>Abrir o guia de iniciação rápida</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation>FAQ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Abrir pasta do yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Capturar tela</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation>Configurar jogo atual..</translation>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroProfile</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Títulos instalados no SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Títulos instalados na NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Títulos do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Adicionar pasta de jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[não definido]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Hat %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Eixo %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Botão %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[desconhecido]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation>Clique 0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation>Clique 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation>Clique 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation>Clique 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation>Clique 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation>Eixo GC %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation>Botão GC %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[não utilizado]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Eixo %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation>Eixo GC %1</translation>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>Ocorreu um erro.
+Tente novamente ou entre em contato com o desenvolvedor do software.
+
+Código de erro: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>Ocorreu um erro em %1 em %2.
+Tente novamente ou entre em contato com o desenvolvedor do software.
+
+Código de erro: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>Ocorreu um erro.
+Código do erro: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Selecione um usuário:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Usuários</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Seletor de perfil</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Digite o texto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Teclado de software</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Digite uma combinação de teclas</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Pilha de chamadas</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>esperando pelo mutex 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>possui os waiters %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>manejo de proprietário: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>esperando por todos os objetos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>esperando por um dos seguintes objetos</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation>[%1]%2 %3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation>não aguardando pelo thread</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>rodando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>pronto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>pausado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>esperando pelo retorno do HLE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>dormindo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>esperando para resposta do IPC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>esperando por objetos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>esperando por mutex</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>aguardando por variável da condição</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>esperando para endereção o árbitro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>inativo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>morto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation>PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>ideal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>núcleo %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Processador desconhecido %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>processador = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>núcleo ideal = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>máscara de afinidade = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>thread id = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>prioridade = %1(atual) / %2(normal)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>últimos ticks executados = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>não aguardando para mutex</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>aguardado pelo thread</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Árvore de espera</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/pt_PT.ts b/dist/languages/pt_PT.ts
new file mode 100644
index 000000000..5ba0bcf3b
--- /dev/null
+++ b/dist/languages/pt_PT.ts
@@ -0,0 +1,4725 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="pt_PT" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>Sobre Yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu é um emulador experimental de código aberto para a Nintendo Switch licenciado sob GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;Este software não deve ser usado para jogar jogos que você não tenha obtido legalmente.&lt;/span&gt;&lt;/p&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Site&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Código Fonte&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contribuidores&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Licença&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; é uma marca comercial da Nintendo. Yuzu não é afiliado com a Nintendo de qualquer forma.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Reportar Compatibilidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Reportar compatibilidade de jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Se você optar por enviar um caso de teste para a&lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;lista de compatibilidade do yuzu&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;As seguintes informações serão coletadas e exibidas no site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Informações de Hardware (CPU / GPU / Sistema operativo)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Que versão do yuzu você está executando&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;A conta yuzu conectada&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Perfeito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Jogo funciona na perfeição sem falhas de áudio ou gráficas.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Ótimo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo funciona com pequenas falhas gráficas ou de áudio e pode ser jogado do início ao fim. Pode exigir algumas soluções alternativas.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>OK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo funciona com grandes falhas gráficas ou de áudio, mas o jogo é jogável do início ao fim com soluções alternativas.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Mau</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo funciona, mas com grandes falhas gráficas ou de áudio. Não é possível progredir em áreas específicas devido a falhas, mesmo com soluções alternativas.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Introdução / Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo não é jogável devido a grandes falhas gráficas ou de áudio. Não é possível passar da tela inicial.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Não Inicia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;O jogo trava ao tentar iniciar.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independente da velocidade ou performance, como este jogo funciona de principio ao fim nesta versão do yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Obrigado pelo seu envio!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Entregando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Erro de comunicação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Ocorreu um erro enquanto enviava o caso de teste</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Próximo</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Motor de Saída:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Este efeito de pós-processamento ajusta a velocidade do áudio para corresponder à velocidade de emulação e ajuda a evitar o engasgue do audio. Isto, no entanto, aumenta a latência de áudio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Activar alongamento de audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Dispositivo de áudio:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Volume:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>Activar GDB Stub</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Porta:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Entrando</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Filtro de registro global</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Mostrar o registro da consola (Apenas Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Abrir a localização do registro</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Argumentos String</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Ativar o Reporte de Serviços detalhado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Isto vai ser resetado automáticamente quando o yuzu fechar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Avançado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Modo Quiosque</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>Configuração yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>IU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Lista de Jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Perfis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Sistema de Ficheiros</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Controlos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Teclas de Atalhos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Depurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Gráficos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Audio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Rede</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Serviços</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Diretórios de armazenamento</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>Cartão SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Cartão de jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Caminho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Inserido</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Jogo Atual</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Gestor de Patch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Dump NSOs Descompactados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>Dump ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Raiz dos Mods</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>Raiz do Dump</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Armazenamento em cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Diretório do cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>Metadata da Lista de Jogos em Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Resetar a Cache da Metadata</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Selecione o Diretório NAND Emulado...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Selecione o Diretório SD Emulado...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>Selecione o Diretório do Cartão de Jogo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Selecionar o diretório do Dump...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>Selecionar o Diretório do Mod Load ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Selecionar o diretório do Cache</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>O cache de metadata já está vazio.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>A operação foi completa com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>Não foi possível excluir o cache de metadata. Pode estar em uso ou inexistente.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Percentagem do limitador de velocidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Confirme a saída enquanto a emulação está em execução</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>Solicitar para o utilizador na inicialização do jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Pausar o emulador quando estiver em segundo plano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Usar cache do disk shader</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Usar emulação assíncrona de GPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Cor de fundo:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Definições de Teclas de Atalho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>Clique duas vezes numa ligação para alterá-la.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Ação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Tecla de Atalho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation> Contexto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Sequência de teclas em conflito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Jogador 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Jogador 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Jogador 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Jogador 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Jogador 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Jogador 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Jogador 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Jogador 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Avançado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Configurar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Configurar Entrada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Analógico Esquerdo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Botôes de Rosto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Analógico Direito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Configurar Rato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Botões do Rato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Frente:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Atrás:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Esquerda:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Meio:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Direita:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Limpar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[não configurado]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Restaurar Padrões</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[pressiona a tecla]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Gestor de Perfis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Utilizador Atual</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Nome de Utilizador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Definir Imagem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Adicionar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Renomear</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Remover</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>O gestor de perfis só está disponível apenas quando o jogo não está em execução.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Introduza o Nome de Utilizador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Utilizadores</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Introduza um nome de utilizador para o novo utilizador:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Introduza um novo nome de utilizador:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Confirmar para eliminar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Estás preste a eliminar o utilizador com o nome &quot;%1&quot;. Tens a certeza?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Definir Imagem de utilizador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>Imagens JPEG (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Error ao eliminar a imagem</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Ocorreu um erro ao tentar substituir imagem anterior em: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Erro ao eliminar o arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Não é possível eliminar o arquivo existente: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Erro ao criar o diretório de imagens do utilizador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Não é possível criar o diretório %1 para armazenar imagens do utilizador.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Erro ao copiar a imagem do utilizador</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Não é possível copiar a imagem de %1 para %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>O BCAT é a forma pela qual a Nintendo envia dados aos jogos para envolver a sua comunidade e desbloquear conteúdos adicionais.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>BCAT Backend</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Saiba mais sobre o BCAT, Boxcat e eventos atuais&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>O serviço boxcat está offline ou não estás ligado à Internet.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Ocorreu um erro ao processar os dados do evento boxcat. Entre em contato com os desenvolvedores do yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>A versão em uso do yuzu é muito nova ou muito antiga para o servidor. Tente atualizar para a ultima versão oficial do yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>De momento, não há eventos no boxcat.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>Yuzu está a atualizar o boxcat ...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Configurações de Sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Nota: isto pode ser substituído quando a configuração da região é de seleção automática</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Japonês (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Inglês</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Francês (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Alemão (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Italiano (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Espanhol (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Chinês</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Coreano (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Holandês (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Português (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Russo (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Taiwanês</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Inglês Britânico</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Francês Canadense</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Espanhol Latino-Americano</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Chinês Simplificado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Chinês Tradicional (正 體 中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>RTC personalizado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Língua</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>Semente de RNG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Mono</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Estéreo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Surround</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>ID da consola:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Modo de saída de som</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM yyyy h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Regenerar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>As configurações do sistema estão disponíveis apenas quando o jogo não está em execução.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Isto substituirá o seu Switch virtual actual por um novo. Seu Switch virtual actual não será recuperável. Isso pode ter efeitos inesperados nos jogos. Isto pode falhar, se você usar uma gravação de jogo de configuração desatualizado. Continuar?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Aviso</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>ID da Consola: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Configurar Ecrã Táctil</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Aviso: As configurações nesta página afectam o funcionamento interno da tela de toque emulada do yuzu. Alterá-los pode resultar em um comportamento indesejável, como a tela de toque não funcionando parcialmente ou até mesmo na sua totatildade. Você só deve usar esta página se souber o que está fazendo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Parâmetros de Toque</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Diâmetro de Toque Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Dedo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Diâmetro de Toque X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Ângulo rotacional</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Restaurar Padrões</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>Geral</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Nota: Alterar o idioma aplicará sua configuração.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Idioma da interface:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Tema:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Lista de Jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Mostrar coluna de Add-Ons</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Tamanho do ícone:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Linha 1 Texto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Linha 2 Texto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Inglês</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Formato</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>Serviço Web do Yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Ao fornecer seu nome de usuário e token, você concorda em permitir que o yuzu colete dados de uso adicionais, que podem incluir informações de identificação do usuário.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Verificar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Inscrever-se</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Token: </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Nome de usuário:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>O que é o meu token?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Compartilhar dados de uso anônimos com a equipa Yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Saber mais</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>ID de Telemetria:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Regenerar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Presença do Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Mostrar o Jogo Atual no seu Estado de Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Saber mais&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Inscrever-se&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;O que é o meu Token?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>ID de Telemetria: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Não especificado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>Token não verificado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>O token não foi verificado. A alteração do token não foi gravada.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>A verificar...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Verificação Falhada</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Verificação Falhada. Verifique se introduziu seu nome de utilizador e o token correctamente e se a conexão com a Internet está operacional.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Dados anônimos são coletados&lt;/a&gt;para ajudar a melhorar o yuzu.&lt;br/&gt;&lt;br/&gt;Gostaria de compartilhar seus dados de uso conosco?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Telemetria</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Falha na verificação de texto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>A Carregar o Web Applet ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>Sair do Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Sair</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>Para sair do aplicativo web, use os controlos fornecidos pelo jogo para selecionar sair, selecione a opção &apos;Sair do Web Applet&apos; na barra de menus ou pressione a tecla &apos;Enter&apos;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Web Applet</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Esta versão do yuzu foi criada sem o suporte ao QtWebEngine, o que significa que o yuzu não pode exibir corretamente o manual do jogo ou a página da web solicitada.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Velocidade da emulação actual. Valores acima ou abaixo de 100% indicam que a emulação está sendo executada mais depressa ou mais devagar do que a Switch</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Quantos quadros por segundo o jogo está exibindo de momento. Isto irá variar de jogo para jogo e de cena para cena.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Tempo gasto para emular um frame da Switch, sem contar o a limitação de quadros ou o v-sync. Para emulação de velocidade máxima, esta deve ser no máximo 16.67 ms.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Limpar Arquivos Recentes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Aviso de Formato de Jogo Desactualizado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Você está usando o formato de directório ROM desconstruído para este jogo, que é um formato desactualizado que foi substituído por outros, como NCA, NAX, XCI ou NSP. Os directórios de ROM não construídos não possuem ícones, metadados e suporte de actualização.&lt;br&gt;&lt;br&gt;Para uma explicação dos vários formatos de Switch que o yuzu suporta,&lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;Verifique a nossa Wiki&lt;/a&gt;. Esta mensagem não será mostrada novamente.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>Erro ao carregar o ROM!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>O formato do ROM não é suportado.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Ocorreu um erro ao inicializar o núcleo do vídeo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu encontrou um erro ao executar o núcleo de vídeo, consulte o log para obter mais detalhes. Para obter mais informações sobre como acessar o log, consulte a seguinte página:&lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Como carregar o arquivo de log&lt;/a&gt;Assegure-se que tem os drivers gráficos mais recentes para sua GPU.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Ocorreu um erro desconhecido. Por favor, veja o log para mais detalhes.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Começar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Save Data</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Mod Data</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Erro ao abrir a pasta %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>A Pasta não existe!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Erro ao abrir os Shader Cache transferíveis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>O Shader Cache para este titulo não existe.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>A Extração de RomFS falhou!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Houve um erro ao copiar os arquivos RomFS ou o usuário cancelou a operação.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Cheio</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>Esqueleto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Selecione o modo de despejo do RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Por favor, selecione a forma como você gostaria que o RomFS fosse despejado&lt;br&gt;Full irá copiar todos os arquivos para o novo diretório enquanto&lt;br&gt;skeleton criará apenas a estrutura de diretórios.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>Extraindo o RomFS ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Cancelar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>Extração de RomFS Bem-Sucedida!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>A operação foi completa com sucesso.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Erro ao abrir %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Selecione o Diretório</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Propriedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>As propriedades do jogo não puderam ser carregadas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Executáveis Switch (%1);;Todos os Ficheiros (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Carregar Arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Abrir o directório ROM extraído</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Diretório inválido selecionado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>O diretório que você selecionou não contém um arquivo &apos;Main&apos;.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Instalando arquivo &quot;%1&quot;...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Aplicação do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Arquivo do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Atualização do aplicativo do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Pacote de Firmware (Tipo A)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Pacote de Firmware (Tipo B)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Actualização do Jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>DLC do Jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Título Delta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Selecione o tipo de instalação do NCA ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Por favor, selecione o tipo de título que você gostaria de instalar este NCA como:
+(Na maioria dos casos, o padrão &apos;Jogo&apos; é suficiente).</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Falha na instalação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>O tipo de título que você selecionou para o NCA é inválido.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Arquivo não encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Arquivo &quot;%1&quot; não encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Continuar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Visualização de erros</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Conta Yuzu Ausente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Para enviar um caso de teste de compatibilidade de jogos, você deve vincular sua conta yuzu.&lt;br&gt;&lt;br/&gt;Para vincular sua conta yuzu, vá para Emulação &amp;gt; Configuração &amp;gt; Rede.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Arquivo Amiibo (%1);; Todos os Arquivos (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Carregar Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Erro ao abrir o arquivo de dados do Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Não é possível abrir o arquivo Amiibo &quot;%1&quot; para leitura.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Erro ao ler o arquivo de dados do Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Não é possível ler completamente os dados do Amiibo. Espera-se que leia %1 bytes, mas só conseguiu ler %2 bytes.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Erro ao carregar dados do Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Não foi possível carregar os dados do Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Captura de Tela</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>Imagem PNG (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Velocidade: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Velocidade: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Jogo: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Quadro: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>O jogo que você está tentando carregar requer arquivos adicionais do seu Switch para serem despejados antes de jogar.&lt;br/&gt;&lt;br/&gt;Para obter mais informações sobre como despejar esses arquivos, consulte a seguinte página da wiki:&lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Despejando arquivos do sistema e as fontes compartilhadas de uma consola Switch&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Você gostaria de regressar para a lista de jogos? Continuar a emulação pode resultar em falhas, dados de salvamento corrompidos ou outros erros.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>O yuzu não conseguiu localizar um arquivo de sistema do Switch. % 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>O yuzu não conseguiu localizar um arquivo de sistema do Switch: %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Arquivo do Sistema Não Encontrado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Arquivo de Sistema em falta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>yuzu não conseguiu localizar as fontes compartilhadas do Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Fontes compartilhadas não encontradas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Fontes compartilhadas em falta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Erro fatal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu encontrou um erro fatal, por favor veja o registro para mais detalhes. Para mais informações sobre como acessar o registro, por favor, veja a seguinte página:&lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Como carregar o arquivo de registro&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Você gostaria de regressar para a lista de jogos? Continuar a emulação pode resultar em falhas, dados de salvamento corrompidos ou outros erros.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Ocorreu um Erro fatal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Confirme a rederivação da chave</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Você está prestes a forçar a rederivação de todas as suas chaves.
+Se você não sabe o que isso significa ou o que você está fazendo,
+esta é uma acção potencialmente destrutiva.
+Por favor, certifique-se que isto é o que você quer
+e opcionalmente faça backups.
+
+Isso irá excluir os seus arquivos de chave gerados automaticamente e executará novamente o módulo de derivação de chave.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Derivando chaves ...
+Isto pode demorar até um minuto, dependendo
+do desempenho do seu sistema.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Derivando Chaves</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Selecione o destino de despejo do RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Por favor, selecione qual o RomFS que você gostaria de despejar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Tem a certeza que quer fechar o yuzu?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Tem a certeza de que quer parar a emulação? Qualquer progresso não salvo será perdido.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>O aplicativo atualmente em execução solicitou que o yuzu não fechasse.
+
+Deseja ignorar isso e sair mesmo assim?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Nome</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Compatibilidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Acrescentos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Tipo de Arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Tamanho</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Abrir Localização de Dados Salvos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Abrir a Localização de Dados do Mod</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Abrir Shader Cache transferíveis</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Despejar RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Copiar título de ID para a área de transferência</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Navegue para a Entrada da Base de Dados de Jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Propriedades</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Examinar Sub-pastas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Remover diretório do Jogo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Abrir Localização do diretório</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Perfeito</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>O Jogo Funciona na Perfeição sem falhas de áudio ou gráficas, todas as funcionalidades testadas funcionam como planeadas sem
+quaisquer soluções alternativas necessárias.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Ótimo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>O Jogo funciona com pequenas falhas gráficas ou de áudio e pode ser jogado do principio ao fim. Pode exigir algumas
+soluções alternativas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Ok</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>O Jogo funciona com grandes falhas gráficas ou de áudio, mas o jogo é jogável do principio ao fim com
+soluções alternativas.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Mau</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>Jogo Funcional, mas com grandes falhas gráficas ou de áudio. Incapaz de progredir em áreas específicas devido a falhas
+mesmo com soluções alternativas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Introdução / Menu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>O Jogo não é jogável devido a grandes falhas gráficas ou de áudio. Não é possível passar da Tela
+Inicial</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Não Inicia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>O jogo trava ao tentar iniciar.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Não Testado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>O jogo ainda não foi testado.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Clique duas vezes para adicionar uma nova pasta à lista de jogos</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Filtro:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Digite o padrão para filtrar</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>A Carregar Shaders 387 / 1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>A Carregar Shaders %v por %m</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Tempo Estimado 5m 4s</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>A Carregar...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>A Carregar Shaders %1 / %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>A iniciar...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Tempo Estimado %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Arquivos Recentes</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Emulação</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Vista</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Depuração</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Ferramentas</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Ajuda</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Carregar Arquivo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Carregar Pasta</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>&amp;Sair</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Começar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Pausa</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Parar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Reinicialize as chaves ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>Sobre yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Modo de Janela Única</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Configurar ...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Exibir Cabeçalhos de Ferramenta de Doca</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Mostrar Barra de Filtros</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Mostrar Barra de Estado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Tela Cheia</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Reiniciar</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Carregar Amiibo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Reportar Compatibilidade</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Abrir Pasta yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Captura de Tela</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>Micro Perfil</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Títulos SD instalados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Títulos NAND instalados</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Títulos do sistema</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Adicionar novo diretório de jogos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[não configurado]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Hat %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Eixo %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Botão %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[Desconhecido]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[sem uso]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Eixo %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>Ocorreu um erro.
+Tente novamente ou entre em contato com o desenvolvedor do software.
+
+Código do Erro: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>Ocorreu um erro em %1 até %2.
+Tente novamente ou entre em contato com o desenvolvedor do software.
+
+Código do Erro: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>Ocorreu um erro.
+Código de Erro: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Selecione um usuário:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Utilizadores</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Selector de perfil</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Digite o texto:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Teclado de Software</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Introduza a tecla de atalho</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Pilha de Chamadas</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>esperando por mutex 0x% 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>has waiters: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>owner handle: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>esperando por todos os objetos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>esperando por todos os objectos</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>correr</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>preparado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>pausado</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>esperando pelo retorno de HLE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>dormindo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>aguardando resposta do IPC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>esperando por objectos</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>esperando por mutex</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>A espera da variável de condição</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>esperando pelo árbitro de endereço</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>dormente</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>morto</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation>PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>ideal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>núcleo %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Processador desconhecido %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>processador = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>núcleo ideal =% 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>máscara de afinidade =% 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>id do segmento =% 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>prioridade =%1(atual) / %2(normal)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>últimos tiques em execução =%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>não esperar por mutex</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>esperado por tópico</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Esquema de espera</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/ru_RU.ts b/dist/languages/ru_RU.ts
new file mode 100644
index 000000000..357b8e416
--- /dev/null
+++ b/dist/languages/ru_RU.ts
@@ -0,0 +1,4720 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="ru_RU" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>О yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu - экспериментальный эмулятор для Nintendo Switch с открытым исходным кодом, лицензированный под GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;Это ПО не должно использоваться для запуска игр, которые были получены нелегально.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Веб-сайт&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Исходный код&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Авторы&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Лицензия&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; является торговой маркой Nintendo. yuzu никак не связан с Nintendo.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>Сообщить о совместимости</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>Сообщить о совместимости игры</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Если вы захотите отправить отчет в &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;Список Совместимости yuzu&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, Следующая информация будет собрана и отображена на сайте:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt; Информация об Аппаратном Обеспечении (ЦП / ГП / Операционная Система)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Версия yuzu&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Подключенный аккаунт yuzu&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>Идеально</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Игра функционирует отлично, без звуковых и графических артефактов.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>Отлично</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Игра работает с небольшими графическими или звуковыми артефактами и может быть пройдена от начала до конца. Могут потребоваться обходные пути.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>Нормально</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Игра работает с существенными графическими или звуковыми артефактами, но с обходными путями может быть пройдена.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>Плохо</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Игра работает, но с существенными графическими или звуковыми артефактами. В некоторых зонах нельзя продвинуться даже с обходными путями.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>Вступление/Меню</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;В игру невозможно играть из-за графических или звуковых артефактов. Невозможно продвинуться дальше Стартового Экрана.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Не Запускается</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Игра падает при старте.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Независимо от скорости и производительности, насколько хорошо эта игра работает от начала до конца в текущей версии yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>Спасибо за ваш отчет!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>Отправка</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>Ошибка соединения</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>Произошла ошибка при отправке Отчета</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>Далее</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>Звук</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>Механизм вывода:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>Этот эффект пост-обработки регулирует скорость звука в соответствии со скоростью эмуляции и помогает предотвратить заикание звука. Это, однако, увеличивает задержку звука.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>Включить растяжение звука</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>Звуковое Устройство:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>Громкость:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>Включить GDB Stub</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>Порт:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>Журналирование</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>Глобальный Фильтр Журнала</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>Показывать Журнал в Консоли (Только Для Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>Открыть Расположение Журнала</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>Homebrew</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>Строка Аргументов</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>Включить Службы Подробных Отчётов</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>Это будет автоматически сброшено после закрытия yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>Дополнительно</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>Параметры yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>Основное</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>Интерфейс</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>Список Игр</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>Система</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>Профили</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>Файловая система</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>Управление</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>Горячие клавиши</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>Отладка</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>Графика</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>Звук</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>Веб</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>Сервисы</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>Директории Хранения</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>SD карта</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>Картридж</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>Путь</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>Вставлен</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>Текущая Игра</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>Управление Патчами</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>Дампить Распакованные NCO</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>Дампить ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>Кэширование</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>Директория Кэша</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>Сбросить Кэш Метаданных</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>Выбрать Папку Для Эмулируемого NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>Выбрать Папку Для Эмулируемого SD</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>Выберите Папку с Дампами</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>Выбрать Папку с Кэшем...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>Кэш метаданных уже пустой.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>Операция выполнена успешно.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>Кэш метаданных не может быть удален. Он может использоваться или отсутствовать.</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>Основное</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>Ограничить Скорость в Процентах</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>Подтверждать выход во время эмуляции</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>Приостанавливать эмуляцию, когда в фоновом режиме</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>Использовать кэш шейдеров на диске</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>Использовать асинхронную эмуляцию ГП</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>Фоновый Цвет:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>Настройки Горячих Клавиш</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>Действие</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>Сочетание</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>Контекст</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>Конфликтующее Сочетание Клавиш</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>Игрок 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>Игрок 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>Игрок 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>Игрок 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>Игрок 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>Игрок 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>Игрок 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>Игрок 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>Дополнительно</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>Настроить</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>Настроить Ввод</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>Левый Стик</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>Основные Кнопки</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>Правый Стик</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>Настроить Мышь</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>Кнопки на Мыши</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>Вперед:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>Назад:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>Левая:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>Средняя:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>Правая:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>Очистить</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[не задано]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>Настройки по Умолчанию</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[нажмите кнопку]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>Управление Профилями</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>Текущий Пользователь</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>Имя Пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>Добавить Изображение</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>Добавить</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>Переименовать</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>Удалить</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>Управление профилями доступно только тогда, когда игра не запущена.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>Введите Имя Пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>Пользователи</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>Введите имя пользователя для нового профиля:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>Введите новое имя пользователя:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>Подтвердите Удаление</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>Вы собираетесь удалить пользователя &quot;%1&quot;. Вы уверены?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>Выберите Изображение для Пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>JPEG Images (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>Ошибка при удалении изображения</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>Ошибка при попытке перезаписи предыдущего изображения в: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>Ошибка при удалении файла</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>Не получилось удалить существующий файл: %1.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>Ошибка при создании папки пользовательских изображений</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>Не получилось создать папку %1 для хранения изображений пользователя.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>Ошибка при копировании изображения пользователя</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>Не получилось скопировать изображение из %1 в %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>BCAT - это способ Nintendo отправлять данные в игры, чтобы привлечь сообщество и разблокировать дополнительный контент.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>Произошла ошибка при обработке данных событий boxcat. Свяжитесь с разработчиками yuzu.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>Используемая вами версия yuzu слишком новая или слишком старая по сравнению с версией на сервере. Попробуйте обновить yuzu до последней официальной версии.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>Настройки Системы</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>Примечание: может быть перезаписано если регион выбирается автоматически</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>Японский (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>Английский (English)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>Французский (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>Немецкий (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>Итальянский (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>Испанский (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>Китайский (Chinese)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>Корейский (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>Голландский (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>Португальский (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>Русский</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>Тайваньский</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>Британский Английский</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>Канадский Французский</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>Латиноамериканский Испанский</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>Упрощенный Китайский</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>Традиционный Китайский (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>Пользовательский RTC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>Язык</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>Зерно RNG</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>Моно</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>Стерео</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>Объёмный звук</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>Идентификатор Консоли:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>Режим вывода звука</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>Перегенерировать</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>Настройки системы доступны только тогда, когда игра не запущена.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>Это заменит ваш текущий виртуальный Switch новым. Ваш текущий виртуальный Switch будет безвозвратно потерян. Это может иметь неожиданные последствия в играх. Может не сработать, если вы используете устаревшую конфигурацию сохраненных игр. Продолжить?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>Внимание</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>Идентификатор Консоли: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>Настроить Сенсорный Экран</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>Внимание: Настройки на этой странице влияют на внутреннюю работу эмулируемого сенсорного экрана yuzu. Их изменение может привести к нежелательному поведению, как например частичная или полная неработоспособность сенсорного экрана. Используйте их, только если вы знаете, что делаете.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>Параметры Касания</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>Диаметр Касания Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>Палец</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>Диаметр Касания X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>Угол Поворота</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>Настройки по Умолчанию</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>Основное</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>Примечание: Изменение языка приведет к применению настроек.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>Язык интерфейса:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>Тема:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>Список Игр</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>Показывать Столбец Дополнений</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>Размер Иконок:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>Текст 1 Строки:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>Текст 2 Строки:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>Английский</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Форма</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>yuzu Веб Сервис</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>Предоставляя ваше имя пользователя и токен, вы разрешаете yuzu собирать дополнительную информацию об использовании, которая может включать данные идентифицирующие пользователя.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>Подтвердить</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>Регистрация</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>Токен:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>Имя Пользователя:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>Что такое токен?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>Телеметрия</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>Делиться анонимной информацией об использовании с командой yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>Узнать больше</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>ID Телеметрии:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>Перегенерировать</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Интеграция с Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>Показывать Текущую Игру в вашем Статусе Discord</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Узнать больше&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Регистрация&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Что такое токен?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>ID Телеметрии: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>Отсутствует</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>Токен не подтвержден</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>Токен не подтвержден. Ваш токен не был изменен.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>Проверка...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>Ошибка подтверждения</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>Ошибка подтверждения. Убедитесь в том, что токен введен корректно, и ваше интернет соединение активно.</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Анонимные данные собираются&lt;/a&gt; для улучшения yuzu. &lt;br/&gt;&lt;br/&gt;Хотели бы вы делиться данными об использовании с нами?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>Телеметрия</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>Ошибка Проверки Текста</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>Выход</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>Эта версия yuzu была собрана без поддержки QtWebEngine, что означает, что yuzu не может нормально отобразить руководство к игре или запрошенную веб-страницу.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>Текущая скорость эмуляции. Значения выше или ниже 100% указывают на то, что эмуляция идет быстрее или медленнее, чем на Switch.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>Количество кадров в секунду в данный момент. Значение будет меняться от игры к игре и от сцене к сцене.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>Время, которое нужно для эмуляции 1 кадра Switch, не принимая во внимание ограничение FPS или v-sync. Для полноскоростной эмуляции значение должно быть не больше 16,67 мс.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>Очистить Недавние Файлы</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>Предупреждение Устаревший Формат Игры</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>Для этой игры вы используете разархивированный формат РОМа, который является устаревшим и был заменен другими, такими как NCA, NAX, XCI или NSP. В разархивированных каталогах РОМа отсутствуют значки, метаданные и поддержка обновлений. &lt;br&gt;&lt;br&gt;Для получения информации о различных форматах Switch, поддерживаемых yuzu, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;просмотрите нашу вики&lt;/a&gt;. Это сообщение больше не будет отображаться.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>Ошибка при загрузке РОМа!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>Формат РОМа не поддерживается.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>Произошла ошибка при инициализации видеоядра.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu обнаружил ошибку во время работы видеоядра. Проверьте журнал для подробностей. Информацию о доступе к журналу можно найти на этой странице: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Как Отправить Файл Журнала.&lt;/a&gt; Убедитесь, что у вас установлены последние графические драйверы.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>Произошла неизвестная ошибка. Пожалуйста, проверьте лог для подробностей.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>Начать</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>Данные Сохранений</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Данные Модов</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>Ошибка при Открытии Папки %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>Папка не существует!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>Ошибка при Открытии Переносного Кеша Шейдеров</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>Шейдерный кеш для этого идентификатора не существует.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>Не удалось извлечь RomFS!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>Произошла ошибка при копировании файлов RomFS или пользователь отменил операцию.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>Полный</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>Выберите Режим Дампа RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>Пожалуйста, выберите, как вы хотите выполнить дамп RomFS. &lt;br&gt;Полное скопирует все файлы в новый каталог, в то время как &lt;br&gt;Структура создаст только структуру каталогов.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>Извлечение RomFS...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>Отмена</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>Извлечение RomFS Прошло Успешно!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>Операция выполнена.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>Ошибка Открытия %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>Выбрать Путь</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>Свойства</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>Не удалось загрузить свойства игры.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Исполняемый файл Switch (%1);;Все файлы (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>Загрузить Файл</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>Открыть Папку Извлечённого РОМа</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>Выбран Неверный Каталог</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>Каталог, который вы выбрали, не содержит «основного» файла.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>Установка файла &quot;%1&quot;...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>Системное Приложение</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>Системный Архив</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>Обновление Системного Приложения</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>Контейнер Прошивки (Тип А)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>Контейнер Прошивки (Тип Б)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>Игра</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>Обновление Игры</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>Загружаемый Контент</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>Delta Название</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>Выберите Тип Установки NCA...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>Пожалуйста, выберите тип приложения, который вы хотите установить для этого NCA:
+ (В большинстве случаев, простое «Игра» подходит.)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>Ошибка Установки</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>Тип приложения, который вы выбрали для NCA, недействителен.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>Файл не найден</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>Файл &quot;%1&quot; не найден</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>Продолжить</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>Показ Ошибок</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>Отсутствует Аккаунт yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>Чтобы отправить отчет о совместимости игры, необходимо привязать свою учетную запись yuzu.&lt;br&gt;&lt;br/&gt;Чтобы привязать свою учетную запись yuzu, перейдите в раздел Эмуляция &amp;gt; Настройка &amp;gt; Веб.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Файл Amiibo (%1);; Все Файлы (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>Загрузить Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>Ошибка открытия файла данных Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>Невозможно открыть файл Amiibo &quot;%1&quot; для чтения.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>Ошибка чтения файла данных Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>Невозможно полностью прочитать данные Amiibo. Ожидалось прочитать %1 байт, но удалось прочитать только %2 байт.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>Ошибка загрузки данных Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>Невозможно загрузить данные Amiibo.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>Сделать Скриншот</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG Image (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>Скорость: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>Скорость: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>Игра: %1 FPS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>Один кадр: %1 ms</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>Игра, которую вы пытаетесь загрузить, требует, чтобы дополнительные файлы были сдамплены с вашего Switch перед началом игры. &lt;br/&gt;&lt;br/&gt;Для получения дополнительной информации о дампе этих файлов см. следующую вики-страницу: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Дамп Системных Архивов и Общих Шрифтов с консоли&lt;/a&gt;. &lt;br/&gt;&lt;br/&gt;Хотите вернуться к списку игр? Продолжение эмуляции может привести к сбоям, повреждению сохраненных данных или другим ошибкам.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>yuzu не удалось найти системный архив Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>yuzu не удалось найти системный архив Switch: %1. %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>Системный Архив Не Найден</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>Отсутствует Системный Архив</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>yuzu не удалось найти общие шрифты Switch. %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>Общие Шрифты Не Найдены</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>Общие Шрифты Отсутствуют</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>Фатальная Ошибка</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu столкнулся с фатальной ошибкой, смотрите подробности в журнале. Информацию о доступе к журналу можно найти на этой странице: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;Как Отправить Файл Журнала&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Хотите вернуться в список игр? Продолжение эмуляции может привести к сбоям, повреждению сохраненных данных или другим ошибкам.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>Обнаружена Фатальная ошибка</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>Подтвердите Перерасчет Ключа</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>Вы собираетесь принудительно пересчитать все ваши ключи.
+Если вы не знаете, что это значит или что вы делаете,
+это потенциально разрушительное действие.
+Пожалуйста, убедитесь, что это то, что вы хотите
+и при желании сделайте резервные копии.
+
+Это удалит ваши автоматически сгенерированные файлы ключей и повторно запустит модуль расчета ключей.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>Получение ключей...
+Это может занять до минуты в зависимости
+от производительности вашей системы.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>Получение Ключей</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>Выберите Цель для Дампа RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>Пожалуйста, выберите, какой RomFS вы хотите сдампить.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>Вы уверены, что хотите закрыть yuzu?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>Вы уверены, что хотите остановить эмуляцию? Любой несохраненный прогресс будет потерян.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>Запущенное в настоящий момент приложение просит yuzu не завершать работу.
+
+Проигнорировать и выйти в любом случае?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>Имя</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>Совместимость</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>Дополнения</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>Тип Файла</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>Размер</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>Открыть Папку Сохранений</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>Открыть Папку Модов</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>Открыть Переносной Кеш Шейдеров</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>Сдампить RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>Скопировать ID приложения в буфер обмена</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>Перейти к записи GameDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>Свойства</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>Сканировать Подпапки</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>Удалить Папку с Играми</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>Открыть Расположение Папки</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>Идеально</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>Игра идёт безупречно, без звуковых или графических артефактов, все протестированные функции работают без обходных путей.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>Отлично</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>Игра работает с небольшими графическими или звуковыми артефактами и может быть пройдена от начала до конца. Могут потребоваться обходные пути.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>Нормально</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>Игра работает с существенными графическими или звуковыми артефактами, но с обходными путями может быть пройдена.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>Плохо</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>Игра работает, но с существенными графическими или звуковыми артефактами. В некоторых зонах нельзя продвинуться даже с обходными путями.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>Вступление/Меню</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>В игру невозможно играть из-за графических или звуковых артефактов. Невозможно продвинуться дальше Стартового Экрана.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>Не Запускается</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>Игра падает при запуске.</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>Не Проверено</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>Игру ещё не проверяли на совместимость.</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>Двойной клик, чтобы добавить новую папку в список игр</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>Поиск:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>Введите текст для поиска</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>Загрузка Шейдера 387 / 1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>Загрузка Шейдера %v из %m</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>Примерное время 5м 4с</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>Загрузка...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>Загрузка Шейдеров %1 / %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>Запуск...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>Примерное Время %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>&amp;Файл</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>Недавние Файлы</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>&amp;Эмуляция</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>&amp;Вид</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>Отладка</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>Инструменты</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>&amp;Помощь</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>Загрузить Файл...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>Загрузить Папку...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>&amp;Выход</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>&amp;Начать</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>&amp;Пауза</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>&amp;Стоп</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>Переинициализировать ключи...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>О yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>Режим Одного Окна</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>Настроить...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>Отображать Заголовки Виджетов Дока</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>Показать Панель Поиска</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>Показать Панель Статуса</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>Полный Экран</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>Перезапустить</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>Загрузить Amiibo…</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>Сообщить о Совместимости</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>Открыть Папку yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>Сделать Скриншот</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>МикроПрофиль</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>Установленные SD Игры</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>Установленные NAND Игры</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>Системные Игры</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>Добавить Новую Папку с Играми</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[не задано]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>Д-Пад %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>Ось %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>Кнопка %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[неизвестно]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[не используется]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>Ось %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>Произошла ошибка.
+Пожалуйста попробуйте еще раз или свяжитесь с разработчиком ПО.
+
+Код Ошибки: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>Произошла ошибка на %1 в %2.
+Пожалуйста попробуйте еще раз или свяжитесь с разработчиком ПО.
+
+Код Ошибки: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>Произошла ошибка.
+Код Ошибки: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>Выберите Пользователя:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>Пользователи</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>Выбор Профиля</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>Введите текст:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>Программная Клавиатура</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>Введите сочетание</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>Стэк вызовов</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>ожидание мьютекса 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>ожидающие: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>ссылка на владельца: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>в ожидании всех объектов</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>в ожидании одного из объектов</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation type="unfinished"/>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation type="unfinished"/>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>работает</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>готов</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>приостановлен</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>ожидание возврата HLE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>сон</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>ожидание ответа IPC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>ожидание объектов</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>ожидание мьютекса</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>ожидание условной переменной </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>ожидание адресного арбитра</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>бездействует</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>мертв</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation> PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>идеально</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>ядро %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>Неизвестный процессор %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>процессор = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>идеальное ядро = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>маска сходства = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>id потока = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>приоритет = %1(текущий) / %2(обычный)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>последние тики = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>не ожидает мьютекс</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>ожидается потоком</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>Дерево Ожидания</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/languages/zh_CN.ts b/dist/languages/zh_CN.ts
new file mode 100644
index 000000000..f953f224a
--- /dev/null
+++ b/dist/languages/zh_CN.ts
@@ -0,0 +1,4747 @@
+<?xml version="1.0" ?><!DOCTYPE TS><TS language="zh_CN" version="2.1">
+<context>
+ <name>AboutDialog</name>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="14"/>
+ <source>About yuzu</source>
+ <translation>关于 yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="30"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;img src=&quot;:/icons/yuzu.png&quot;/&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="60"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:28pt;&quot;&gt;yuzu&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="73"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;%1 | %2-%3 (%4)&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="86"/>
+ <source>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:'Ubuntu'; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:'MS Shell Dlg 2'; font-size:12pt;&quot;&gt;yuzu is an experimental open-source emulator for the Nintendo Switch licensed under GPLv2.0.&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:'MS Shell Dlg 2'; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;This software should not be used to play games you have not legally obtained.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;!DOCTYPE HTML PUBLIC &quot;-//W3C//DTD HTML 4.0//EN&quot; &quot;http://www.w3.org/TR/REC-html40/strict.dtd&quot;&gt;
+&lt;html&gt;&lt;head&gt;&lt;meta name=&quot;qrichtext&quot; content=&quot;1&quot; /&gt;&lt;style type=&quot;text/css&quot;&gt;
+p, li { white-space: pre-wrap; }
+&lt;/style&gt;&lt;/head&gt;&lt;body style=&quot; font-family:&apos;Ubuntu&apos;; font-size:11pt; font-weight:400; font-style:normal;&quot;&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;yuzu 是一个实验性的开源 Nintendo Switch 模拟器,以 GPLv2.0+ 授权。&lt;/span&gt;&lt;/p&gt;
+&lt;p style=&quot;-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px; font-family:&apos;MS Shell Dlg 2&apos;; font-size:8pt;&quot;&gt;&lt;br /&gt;&lt;/p&gt;
+&lt;p style=&quot; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;&lt;span style=&quot; font-family:&apos;MS Shell Dlg 2&apos;; font-size:12pt;&quot;&gt;这个软件不应该用来运行非法取得的游戏。&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="118"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Website&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Source Code&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;Contributors&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;License&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;网站&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;源代码&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/graphs/contributors&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;贡献者&lt;/span&gt;&lt;/a&gt; | &lt;a href=&quot;https://github.com/yuzu-emu/yuzu/blob/master/license.txt&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#039be5;&quot;&gt;许可证&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/aboutdialog.ui" line="134"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; is a trademark of Nintendo. yuzu is not affiliated with Nintendo in any way.&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:7pt;&quot;&gt;&amp;quot;Nintendo Switch&amp;quot; 是任天堂的商标。yuzu 与任天堂没有任何关系。&lt;/span&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>CalibrationConfigurationDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="25"/>
+ <source>Communicating with the server...</source>
+ <translation>正在与服务器通信…</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="26"/>
+ <source>Cancel</source>
+ <translation>取消</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="44"/>
+ <source>Touch the top left corner &lt;br&gt;of your touchpad.</source>
+ <translation>触摸你的触摸板&lt;br&gt;的左上角。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="47"/>
+ <source>Now touch the bottom right corner &lt;br&gt;of your touchpad.</source>
+ <translation>触摸你的触摸板&lt;br&gt;的右下角。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="50"/>
+ <source>Configuration completed!</source>
+ <translation>配置完成!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="55"/>
+ <source>OK</source>
+ <translation>确定</translation>
+ </message>
+</context>
+<context>
+ <name>CompatDB</name>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="20"/>
+ <source>Report Compatibility</source>
+ <translation>报告兼容性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="27"/>
+ <location filename="../../src/yuzu/compatdb.ui" line="63"/>
+ <source>Report Game Compatibility</source>
+ <translation>报告游戏兼容性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="36"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;Should you choose to submit a test case to the &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu Compatibility List&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;, The following information will be collected and displayed on the site:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Hardware Information (CPU / GPU / Operating System)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;Which version of yuzu you are running&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;The connected yuzu account&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;如果您选择向 &lt;/span&gt;&lt;a href=&quot;https://yuzu-emu.org/game/&quot;&gt;&lt;span style=&quot; font-size:10pt; text-decoration: underline; color:#0000ff;&quot;&gt;yuzu 兼容性列表&lt;/span&gt;&lt;/a&gt;&lt;span style=&quot; font-size:10pt;&quot;&gt;提交测试用例的话,以下信息将会被收集并显示在网站上:&lt;/span&gt;&lt;/p&gt;&lt;ul style=&quot;margin-top: 0px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; -qt-list-indent: 1;&quot;&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;设备硬件信息 (CPU / GPU / 操作系统)&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;您正在使用的 yuzu 版本&lt;/li&gt;&lt;li style=&quot; margin-top:12px; margin-bottom:12px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;&quot;&gt;已关联的 yuzu 账户信息&lt;/li&gt;&lt;/ul&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="72"/>
+ <source>Perfect</source>
+ <translation>完美</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions flawlessly with no audio or graphical glitches.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;游戏运行完美,没有音频或图形问题。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="89"/>
+ <source>Great </source>
+ <translation>良好</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="96"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with minor graphical or audio glitches and is playable from start to finish. May require some workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;游戏运行时会有非常轻微的图像或音频问题,但是能从头玩到尾。可能需要一些技巧才能完成游戏。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="106"/>
+ <source>Okay</source>
+ <translation>一般</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="113"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions with major graphical or audio glitches, but game is playable from start to finish with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;游戏运行时会有很多图像或音频错误,但是在使用一些特殊技巧之后能完整地完成游戏。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="123"/>
+ <source>Bad</source>
+ <translation>较差</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="130"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches even with workarounds.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;游戏能运行,但是会有大量图像或音频错误。即使使用一些技巧也无法通过游戏的某些区域。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="140"/>
+ <source>Intro/Menu</source>
+ <translation>开场 / 菜单</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="147"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start Screen.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;游戏完全没法玩,图像或音频有重大错误。通过开场菜单后无法继续。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="157"/>
+ <source>Won&apos;t Boot</source>
+ <translation>无法打开</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="170"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;The game crashes when attempting to startup.&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;在启动游戏时直接崩溃了。&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="182"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;Independent of speed or performance, how well does this game play from start to finish on this version of yuzu?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;body&gt;&lt;html&gt;&lt;head/&gt;&lt;p&gt;在不考虑速度或帧率的情况下,使用此版本 yuzu 玩这款游戏的情况如何?&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.ui" line="206"/>
+ <source>Thank you for your submission!</source>
+ <translation>感谢您向我们提交信息!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="61"/>
+ <source>Submitting</source>
+ <translation>提交中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="74"/>
+ <source>Communication error</source>
+ <translation>网络错误</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="75"/>
+ <source>An error occured while sending the Testcase</source>
+ <translation>在提交测试用例时发生错误。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/compatdb.cpp" line="77"/>
+ <source>Next</source>
+ <translation>下一步</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureAudio</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="17"/>
+ <source>Audio</source>
+ <translation>声音</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="25"/>
+ <source>Output Engine:</source>
+ <translation>输出引擎:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="37"/>
+ <source>This post-processing effect adjusts audio speed to match emulation speed and helps prevent audio stutter. This however increases audio latency.</source>
+ <translation>这种后处理效果可以调整音频速度以匹配模拟速度,并有助于防止音频卡顿。 但是会增加音频延迟。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="40"/>
+ <source>Enable audio stretching</source>
+ <translation>启动音频拉伸</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="49"/>
+ <source>Audio Device:</source>
+ <translation>音频设备:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="77"/>
+ <source>Use global volume</source>
+ <translation>使用全局音量</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="82"/>
+ <source>Set volume:</source>
+ <translation>音量:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="90"/>
+ <source>Volume:</source>
+ <translation>音量:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.ui" line="135"/>
+ <source>0 %</source>
+ <translation>0 %</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_audio.cpp" line="98"/>
+ <source>%1%</source>
+ <comment>Volume percentage (e.g. 50%)</comment>
+ <translation>%1%</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpu</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="22"/>
+ <source>General</source>
+ <translation>通用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="30"/>
+ <source>Accuracy:</source>
+ <translation>精度:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="38"/>
+ <source>Accurate</source>
+ <translation>精确</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="43"/>
+ <source>Unsafe</source>
+ <translation>低精度</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="48"/>
+ <source>Enable Debug Mode</source>
+ <translation>启用调试模式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="61"/>
+ <source>We recommend setting accuracy to &quot;Accurate&quot;.</source>
+ <translation>我们推荐将精度设置为“精确”。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="75"/>
+ <source>Unsafe CPU Optimization Settings</source>
+ <translation>低精度 CPU 优化选项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="84"/>
+ <source>These settings reduce accuracy for speed.</source>
+ <translation>这些设置项提高了速度,但精度有所降低。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="91"/>
+ <source>Unfuse FMA (improve performance on CPUs without FMA)</source>
+ <translation>低精度 FMA (在 CPU 不支持 FMA 指令集的情况下提高性能)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="94"/>
+ <source>
+ &lt;div&gt;This option improves speed by reducing accuracy of fused-multiply-add instructions on CPUs without native FMA support.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;该选项通过降低积和熔加运算的精度而提高模拟器在不支持 FMA 指令集 CPU 上的运行速度。&lt;/div&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="103"/>
+ <source>Faster FRSQRTE and FRECPE</source>
+ <translation>快速 FRSQRTE 和 FRECPE</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="106"/>
+ <source>
+ &lt;div&gt;This option improves the speed of some approximate floating-point functions by using less accurate native approximations.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;该选项通过使用精度较低的近似值来提高某些浮点函数的运算速度。&lt;/div&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.ui" line="133"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>只有当游戏不在运行时,CPU 设置才可用。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="43"/>
+ <source>Setting CPU to Debug Mode</source>
+ <translation>CPU 调试模式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu.cpp" line="44"/>
+ <source>CPU Debug Mode is only intended for developer use. Are you sure you want to enable this?</source>
+ <translation>CPU 调试模式仅用于开发人员。您确定要打开这个选项吗?</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureCpuDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="22"/>
+ <source>Toggle CPU Optimizations</source>
+ <translation>切换 CPU 优化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="31"/>
+ <source>
+ &lt;div&gt;
+ &lt;b&gt;For debugging only.&lt;/b&gt;
+ &lt;br&gt;
+ If you're not sure what these do, keep all of these enabled.
+ &lt;br&gt;
+ These settings only take effect when CPU Accuracy is &quot;Debug Mode&quot;.
+ &lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;
+&lt;b&gt;仅供调试使用。&lt;/b&gt;
+&lt;br&gt;
+如果您不确定这些选项的作用,则保持它们的启用状态。
+&lt;br&gt;
+这些选项仅在 CPU 精度处于“调试模式”时生效。
+&lt;/div&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="46"/>
+ <source>Enable inline page tables</source>
+ <translation>启用内嵌页表</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="49"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;This optimization speeds up memory accesses by the guest program.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Enabling it inlines accesses to PageTable::pointers into emitted code.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;Disabling this forces all memory accesses to go through the Memory::Read/Memory::Write functions.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div style=&quot;white-space: nowrap&quot;&gt;这个选项提升了来宾程序的内存访问速度。&lt;/div&gt;
+&lt;div style=&quot;white-space: nowrap&quot;&gt;启用内嵌到 PageTable::指向已发射代码的指针。&lt;/div&gt;
+&lt;div style=&quot;white-space: nowrap&quot;&gt;禁用此选项将强制通过 Memory::Read/Memory::Write 函数进行内存访问。&lt;/div&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="60"/>
+ <source>Enable block linking</source>
+ <translation>启用块链接</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="63"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by allowing emitted basic blocks to jump directly to other basic blocks if the destination PC is static.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;该选项通过允许发出的基本块直接跳转到其他基本块(如果目标 PC 是静态的)来避免调度器的查找。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="72"/>
+ <source>Enable return stack buffer</source>
+ <translation>启用返回堆栈缓冲区</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="75"/>
+ <source>
+ &lt;div&gt;This optimization avoids dispatcher lookups by keeping track potential return addresses of BL instructions. This approximates what happens with a return stack buffer on a real CPU.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;该选项通过跟踪 BL 指令的潜在返回地址来避免调度器查找。这近似于 CPU 返回堆栈缓冲区的情况。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="84"/>
+ <source>Enable fast dispatcher</source>
+ <translation>启用快速调度</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="87"/>
+ <source>
+ &lt;div&gt;Enable a two-tiered dispatch system. A faster dispatcher written in assembly has a small MRU cache of jump destinations is used first. If that fails, dispatch falls back to the slower C++ dispatcher.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;启用两层调度系统。首先使用一个更快的调度程序跳转至目标 MRU 缓存。如果失败,调度返回到较慢的 C++ 调度程序。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="96"/>
+ <source>Enable context elimination</source>
+ <translation>启用上下文消除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="99"/>
+ <source>
+ &lt;div&gt;Enables an IR optimization that reduces unnecessary accesses to the CPU context structure.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;启用 IR 优化,以减少 CPU 对上下文结构的不必要访问。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="108"/>
+ <source>Enable constant propagation</source>
+ <translation>启用恒定传播</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="111"/>
+ <source>
+ &lt;div&gt;Enables IR optimizations that involve constant propagation.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;启用涉及恒定传播的 IR 优化。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="120"/>
+ <source>Enable miscellaneous optimizations</source>
+ <translation>启用其他优化</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="123"/>
+ <source>
+ &lt;div&gt;Enables miscellaneous IR optimizations.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div&gt;启用其他的 IR 优化。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="132"/>
+ <source>Enable misalignment check reduction</source>
+ <translation>减少偏差检查</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="135"/>
+ <source>
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When enabled, a misalignment is only triggered when an access crosses a page boundary.&lt;/div&gt;
+ &lt;div style=&quot;white-space: nowrap&quot;&gt;When disabled, a misalignment is triggered on all misaligned accesses.&lt;/div&gt;
+ </source>
+ <translation>
+&lt;div style=&quot;white-space: nowrap&quot;&gt;启用时,只有当访问越过页面边界时才会触发偏移。&lt;/div&gt;
+&lt;div style=&quot;white-space: nowrap&quot;&gt;禁用时,所有未对齐的访问都会触发偏移。&lt;/div&gt;
+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_cpu_debug.ui" line="163"/>
+ <source>CPU settings are available only when game is not running.</source>
+ <translation>只有当游戏不在运行时,CPU 设置才可用。</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebug</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="22"/>
+ <source>GDB</source>
+ <translation>GDB</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="30"/>
+ <source>Enable GDB Stub</source>
+ <translation>开启 GDB 调试</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="50"/>
+ <source>Port:</source>
+ <translation>端口:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="71"/>
+ <source>Logging</source>
+ <translation>日志</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="79"/>
+ <source>Global Log Filter</source>
+ <translation>全局日志过滤器</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="93"/>
+ <source>Show Log Console (Windows Only)</source>
+ <translation>显示日志窗口(仅限 Windows)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="100"/>
+ <source>Open Log Location</source>
+ <translation>打开日志位置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="112"/>
+ <source>Homebrew</source>
+ <translation>自制游戏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="120"/>
+ <source>Arguments String</source>
+ <translation>参数字符串</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="135"/>
+ <source>Graphics</source>
+ <translation>图形</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="144"/>
+ <source>When checked, the graphics API enters in a slower debugging mode</source>
+ <translation>启用时,图形 API 将进入较慢的调试模式。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="147"/>
+ <source>Enable Graphics Debugging</source>
+ <translation>启用图形调试</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="157"/>
+ <source>When checked, it disables the macro Just In Time compiler. Enabled this makes games run slower</source>
+ <translation>启用时,将禁用宏即时编译器。这会降低游戏运行速度。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="160"/>
+ <source>Disable Macro JIT</source>
+ <translation>禁用宏 JIT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="170"/>
+ <source>Dump</source>
+ <translation>转储</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="176"/>
+ <source>Enable Verbose Reporting Services</source>
+ <translation>启用详细报告服务</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="188"/>
+ <source>This will be reset automatically when yuzu closes.</source>
+ <translation>当 yuzu 关闭时会自动重置。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="201"/>
+ <source>Advanced</source>
+ <translation>高级选项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug.ui" line="207"/>
+ <source>Kiosk (Quest) Mode</source>
+ <translation>Kiosk (Quest) 模式</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDebugController</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="14"/>
+ <source>Configure Debug Controller</source>
+ <translation>调试控制器设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="40"/>
+ <source>Clear</source>
+ <translation>清除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_debug_controller.ui" line="47"/>
+ <source>Defaults</source>
+ <translation>系统默认</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureDialog</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="20"/>
+ <source>yuzu Configuration</source>
+ <translation>yuzu 设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="48"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="51"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="86"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="120"/>
+ <source>General</source>
+ <translation>通用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="132"/>
+ <source>UI</source>
+ <translation>界面</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="59"/>
+ <source>Game List</source>
+ <translation>游戏列表</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="64"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="67"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="121"/>
+ <source>System</source>
+ <translation>系统</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="72"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="75"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="122"/>
+ <source>Profiles</source>
+ <translation>用户配置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="80"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="83"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="133"/>
+ <source>Filesystem</source>
+ <translation>文件系统</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="91"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="123"/>
+ <source>Controls</source>
+ <translation>控制</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="99"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="124"/>
+ <source>Hotkeys</source>
+ <translation>热键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="104"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="107"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="125"/>
+ <source>CPU</source>
+ <translation>CPU</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="112"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="115"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="144"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="147"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="126"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="130"/>
+ <source>Debug</source>
+ <translation>调试</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="120"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="123"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="89"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="127"/>
+ <source>Graphics</source>
+ <translation>图形</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="128"/>
+ <source>Advanced</source>
+ <translation>高级选项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="131"/>
+ <source>GraphicsAdvanced</source>
+ <translation>高级图形选项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="136"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="139"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="90"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="129"/>
+ <source>Audio</source>
+ <translation>声音</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="152"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="155"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="131"/>
+ <source>Web</source>
+ <translation>网络</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="160"/>
+ <location filename="../../src/yuzu/configuration/configure.ui" line="163"/>
+ <location filename="../../src/yuzu/configuration/configure_dialog.cpp" line="134"/>
+ <source>Services</source>
+ <translation>服务</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureFilesystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="22"/>
+ <source>Storage Directories</source>
+ <translation>存储目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="28"/>
+ <source>NAND</source>
+ <translation>NAND</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="35"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="111"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="140"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="230"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="48"/>
+ <source>SD Card</source>
+ <translation>SD 卡</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="81"/>
+ <source>Gamecard</source>
+ <translation>游戏卡带</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="87"/>
+ <source>Path</source>
+ <translation>路径</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="97"/>
+ <source>Inserted</source>
+ <translation>已插入</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="104"/>
+ <source>Current Game</source>
+ <translation>当前游戏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="121"/>
+ <source>Patch Manager</source>
+ <translation>补丁管理</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="149"/>
+ <source>Dump Decompressed NSOs</source>
+ <translation>转储已解压的 NSO 文件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="156"/>
+ <source>Dump ExeFS</source>
+ <translation>转储 ExeFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="165"/>
+ <source>Mod Load Root</source>
+ <translation>Mod 加载根目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="172"/>
+ <source>Dump Root</source>
+ <translation>转储根目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="198"/>
+ <source>Caching</source>
+ <translation>缓存中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="204"/>
+ <source>Cache Directory</source>
+ <translation>缓存目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="239"/>
+ <source>Cache Game List Metadata</source>
+ <translation>缓存游戏列表数据</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.ui" line="246"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="128"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="133"/>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="138"/>
+ <source>Reset Metadata Cache</source>
+ <translation>重置缓存数据</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="92"/>
+ <source>Select Emulated NAND Directory...</source>
+ <translation>选择模拟 NAND 目录...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="95"/>
+ <source>Select Emulated SD Directory...</source>
+ <translation>选择模拟 SD 卡目录...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="98"/>
+ <source>Select Gamecard Path...</source>
+ <translation>选择游戏卡带路径...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="101"/>
+ <source>Select Dump Directory...</source>
+ <translation>选择转储目录...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="104"/>
+ <source>Select Mod Load Directory...</source>
+ <translation>选择 Mod 载入目录...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="107"/>
+ <source>Select Cache Directory...</source>
+ <translation>选择缓存目录...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="129"/>
+ <source>The metadata cache is already empty.</source>
+ <translation>缓存数据已为空。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="134"/>
+ <source>The operation completed successfully.</source>
+ <translation>操作已成功完成。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_filesystem.cpp" line="139"/>
+ <source>The metadata cache couldn&apos;t be deleted. It might be in use or non-existent.</source>
+ <translation>缓存数据删除失败。它可能不存在或正在被使用。</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGeneral</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="22"/>
+ <source>General</source>
+ <translation>通用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="32"/>
+ <source>Limit Speed Percent</source>
+ <translation>运行速度限制</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="39"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="57"/>
+ <source>Multicore CPU Emulation</source>
+ <translation>多核 CPU 仿真</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="64"/>
+ <source>Confirm exit while emulation is running</source>
+ <translation>在游戏运行时退出需要确认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="71"/>
+ <source>Prompt for user on game boot</source>
+ <translation>游戏启动时提示选择用户</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="78"/>
+ <source>Pause emulation when in background</source>
+ <translation>模拟器位于后台时暂停模拟</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_general.ui" line="85"/>
+ <source>Hide mouse on inactivity</source>
+ <translation>自动隐藏鼠标光标</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphics</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="22"/>
+ <source>API Settings</source>
+ <translation>API 设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="46"/>
+ <source>API:</source>
+ <translation>API:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="67"/>
+ <source>Device:</source>
+ <translation>设备:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="83"/>
+ <source>Graphics Settings</source>
+ <translation>图形设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="89"/>
+ <source>Use disk shader cache</source>
+ <translation>使用磁盘着色器缓存</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="96"/>
+ <source>Use asynchronous GPU emulation</source>
+ <translation>使用 GPU 异步模拟</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="118"/>
+ <source>Aspect Ratio:</source>
+ <translation>屏幕纵横比:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="126"/>
+ <source>Default (16:9)</source>
+ <translation>默认 (16:9)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="131"/>
+ <source>Force 4:3</source>
+ <translation>强制 4:3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="136"/>
+ <source>Force 21:9</source>
+ <translation>强制 21:9</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="141"/>
+ <source>Stretch to Window</source>
+ <translation>拉伸窗口</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="186"/>
+ <source>Use global background color</source>
+ <translation>使用全局背景颜色</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="191"/>
+ <source>Set background color:</source>
+ <translation>设置背景颜色:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.ui" line="199"/>
+ <source>Background Color:</source>
+ <translation>背景颜色:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics.cpp" line="196"/>
+ <source>OpenGL Graphics Device</source>
+ <translation>OpenGL 图形设备</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureGraphicsAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="22"/>
+ <source>Advanced Graphics Settings</source>
+ <translation>高级图形选项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="43"/>
+ <source>Accuracy Level:</source>
+ <translation>精度:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="72"/>
+ <source>VSync prevents the screen from tearing, but some graphics cards have lower performance with VSync enabled. Keep it enabled if you don&apos;t notice a performance difference.</source>
+ <translation>垂直同步可防止画面产生撕裂感。但启用垂直同步后,某些设备性能可能会有所降低。如果您没有感到性能差异,请保持启用状态。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="75"/>
+ <source>Use VSync (OpenGL only)</source>
+ <translation>启用垂直同步 (仅限 OpenGL 模式)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="82"/>
+ <source>Enabling this reduces shader stutter. Enables OpenGL assembly shaders on supported Nvidia devices (NV_gpu_program5 is required). This feature is experimental.</source>
+ <translation>在支持 NV_gpu_program5 的 Nvidia 设备上启用 OpenGL 程序集着色器。启用此项将减少着色器卡顿。实验性功能。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="85"/>
+ <source>Use assembly shaders (experimental, Nvidia OpenGL only)</source>
+ <translation>启用程序集着色器 (实验性,仅限 Nvidia OpenGL)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="92"/>
+ <source>Enables asynchronous shader compilation, which may reduce shader stutter. This feature is experimental.</source>
+ <translation>启用异步着色器编译,这可能会减少着色器卡顿。实验性功能。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="95"/>
+ <source>Use asynchronous shader building (experimental)</source>
+ <translation>启用异步着色器编译 (实验性)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="102"/>
+ <source>Use Fast GPU Time</source>
+ <translation>启用快速 GPU 时钟</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="124"/>
+ <source>Anisotropic Filtering:</source>
+ <translation>各向异性过滤:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="132"/>
+ <source>Default</source>
+ <translation>系统默认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="137"/>
+ <source>2x</source>
+ <translation>2×</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="142"/>
+ <source>4x</source>
+ <translation>4×</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="147"/>
+ <source>8x</source>
+ <translation>8×</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_graphics_advanced.ui" line="152"/>
+ <source>16x</source>
+ <translation>16×</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureHotkeys</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="14"/>
+ <source>Hotkey Settings</source>
+ <translation>热键设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="22"/>
+ <source>Double-click on a binding to change it.</source>
+ <translation>双击已绑定的项目以改变设定。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="42"/>
+ <source>Clear All</source>
+ <translation>全部清除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.ui" line="49"/>
+ <source>Restore Defaults</source>
+ <translation>恢复默认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Action</source>
+ <translation>作用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Hotkey</source>
+ <translation>热键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="74"/>
+ <source>Context</source>
+ <translation>位置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="96"/>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="183"/>
+ <source>Conflicting Key Sequence</source>
+ <translation>按键冲突</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="97"/>
+ <source>The entered key sequence is already assigned to: %1</source>
+ <translation>输入的密钥序列已分配给: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="171"/>
+ <source>Restore Default</source>
+ <translation>恢复默认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="172"/>
+ <source>Clear</source>
+ <translation>清除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_hotkeys.cpp" line="184"/>
+ <source>The default key sequence is already assigned to: %1</source>
+ <translation>默认密钥序列已分配给: %1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInput</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="14"/>
+ <source>ConfigureInput</source>
+ <translation>输入设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="39"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="42"/>
+ <source>Player 1</source>
+ <translation>玩家 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="47"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="50"/>
+ <source>Player 2</source>
+ <translation>玩家 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="55"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="58"/>
+ <source>Player 3</source>
+ <translation>玩家 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="63"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="66"/>
+ <source>Player 4</source>
+ <translation>玩家 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="74"/>
+ <source>Player 5</source>
+ <translation>玩家 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="82"/>
+ <source>Player 6</source>
+ <translation>玩家 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="87"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="90"/>
+ <source>Player 7</source>
+ <translation>玩家 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="95"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="98"/>
+ <source>Player 8</source>
+ <translation>玩家 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="106"/>
+ <source>Advanced</source>
+ <translation>高级</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="138"/>
+ <source>Console Mode</source>
+ <translation>控制台模式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="159"/>
+ <source>Docked</source>
+ <translation>Docked</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="169"/>
+ <source>Undocked</source>
+ <translation>Undocked</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="179"/>
+ <source>Vibration</source>
+ <translation>震动</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="212"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="231"/>
+ <source>Motion</source>
+ <translation>体感</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="267"/>
+ <source>Configure</source>
+ <translation>设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="302"/>
+ <source>Controllers</source>
+ <translation>控制器</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="330"/>
+ <source>1</source>
+ <translation>1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="371"/>
+ <source>2</source>
+ <translation>2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="381"/>
+ <source>3</source>
+ <translation>3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="391"/>
+ <source>4</source>
+ <translation>4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="401"/>
+ <source>5</source>
+ <translation>5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="411"/>
+ <source>6</source>
+ <translation>6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="421"/>
+ <source>7</source>
+ <translation>7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="431"/>
+ <source>8</source>
+ <translation>8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="441"/>
+ <source>Connected</source>
+ <translation>已连接</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="500"/>
+ <source>Defaults</source>
+ <translation>系统默认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input.ui" line="543"/>
+ <source>Clear</source>
+ <translation>清除</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>输入设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="74"/>
+ <source>Joycon Colors</source>
+ <translation>Joycon 颜色</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="125"/>
+ <source>Player 1</source>
+ <translation>玩家 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="164"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="450"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="754"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1040"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1365"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1651"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1955"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2241"/>
+ <source>L Body</source>
+ <translation>左侧主体</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="219"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="505"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="809"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1095"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1420"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1706"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2010"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2296"/>
+ <source>L Button</source>
+ <translation>左侧按键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="295"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="581"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="885"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1171"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1496"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1782"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2086"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2372"/>
+ <source>R Body</source>
+ <translation>右侧主体</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="350"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="636"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="940"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1226"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1551"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1837"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2141"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2427"/>
+ <source>R Button</source>
+ <translation>右侧按键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="411"/>
+ <source>Player 2</source>
+ <translation>玩家 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="715"/>
+ <source>Player 3</source>
+ <translation>玩家 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1001"/>
+ <source>Player 4</source>
+ <translation>玩家 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1326"/>
+ <source>Player 5</source>
+ <translation>玩家 5</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1612"/>
+ <source>Player 6</source>
+ <translation>玩家 6</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="1916"/>
+ <source>Player 7</source>
+ <translation>玩家 7</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2202"/>
+ <source>Player 8</source>
+ <translation>玩家 8</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2533"/>
+ <source>Other</source>
+ <translation>其他</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2545"/>
+ <source>Keyboard</source>
+ <translation>键盘</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2552"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2575"/>
+ <source>Advanced</source>
+ <translation>高级选项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2582"/>
+ <source>Touchscreen</source>
+ <translation>触摸屏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2595"/>
+ <source>Mouse</source>
+ <translation>鼠标</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2602"/>
+ <source>Motion / Touch</source>
+ <translation>体感/触摸</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2609"/>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2623"/>
+ <source>Configure</source>
+ <translation>设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_advanced.ui" line="2616"/>
+ <source>Debug Controller</source>
+ <translation>控制器调试</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureInputPlayer</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="14"/>
+ <source>Configure Input</source>
+ <translation>输入设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="63"/>
+ <source>Connect Controller</source>
+ <translation>已连接控制器</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="88"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="358"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="379"/>
+ <source>Pro Controller</source>
+ <translation>Pro Controller</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="93"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="359"/>
+ <source>Dual Joycons</source>
+ <translation>双 Joycons 手柄</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="98"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="360"/>
+ <source>Left Joycon</source>
+ <translation>左 Joycon 手柄</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="103"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="361"/>
+ <source>Right Joycon</source>
+ <translation>右 Joycon 手柄</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="108"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="365"/>
+ <source>Handheld</source>
+ <translation>掌机模式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="119"/>
+ <source>Input Device</source>
+ <translation>输入设备</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="141"/>
+ <source>Any</source>
+ <translation>任意</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="146"/>
+ <source>Keyboard/Mouse</source>
+ <translation>键盘/鼠标</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="182"/>
+ <source>Profile</source>
+ <translation>用户配置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="215"/>
+ <source>Save</source>
+ <translation>保存</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="231"/>
+ <source>New</source>
+ <translation>新建</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="247"/>
+ <source>Delete</source>
+ <translation>删除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="310"/>
+ <source>Left Stick</source>
+ <translation>左摇杆</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="368"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="410"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="944"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="983"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2457"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2496"/>
+ <source>Up</source>
+ <translation>上</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="441"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="480"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1014"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1053"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2527"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2566"/>
+ <source>Left</source>
+ <translation>左</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="490"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="529"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1063"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2576"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2615"/>
+ <source>Right</source>
+ <translation>右</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="572"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="611"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1145"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1184"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2658"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2697"/>
+ <source>Down</source>
+ <translation>下</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="642"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="681"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2728"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2767"/>
+ <source>Pressed</source>
+ <translation>按下</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="691"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="730"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2777"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2816"/>
+ <source>Modifier</source>
+ <translation>轻推</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="740"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2826"/>
+ <source>Range</source>
+ <translation>灵敏度</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="773"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2859"/>
+ <source>%</source>
+ <translation>%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="816"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2899"/>
+ <source>Deadzone: 0%</source>
+ <translation>摇杆死区:0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="840"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2923"/>
+ <source>Modifier Range: 0%</source>
+ <translation>摇杆灵敏度:0%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="886"/>
+ <source>D-Pad</source>
+ <translation>十字方向键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1270"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1309"/>
+ <source>L</source>
+ <translation>L</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1319"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1358"/>
+ <source>ZL</source>
+ <translation>ZL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1423"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1462"/>
+ <source>Minus</source>
+ <translation>-</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1472"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1511"/>
+ <source>Capture</source>
+ <translation>截图</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1542"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1581"/>
+ <source>Plus</source>
+ <translation>+</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1591"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1630"/>
+ <source>Home</source>
+ <translation>Home</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1695"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1734"/>
+ <source>R</source>
+ <translation>R</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1744"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1783"/>
+ <source>ZR</source>
+ <translation>ZR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1848"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1887"/>
+ <source>SL</source>
+ <translation>SL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1897"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="1936"/>
+ <source>SR</source>
+ <translation>SR</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2044"/>
+ <source>Face Buttons</source>
+ <translation>主要按键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2102"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2141"/>
+ <source>X</source>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2172"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2211"/>
+ <source>Y</source>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2221"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2260"/>
+ <source>A</source>
+ <translation>A</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2303"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2342"/>
+ <source>B</source>
+ <translation>B</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.ui" line="2390"/>
+ <source>Right Stick</source>
+ <translation>右摇杆</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="338"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="618"/>
+ <source>Deadzone: %1%</source>
+ <translation>摇杆死区:%1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="345"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="629"/>
+ <source>Modifier Range: %1%</source>
+ <translation>摇杆灵敏度:%1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="662"/>
+ <source>[waiting]</source>
+ <translation>[等待中]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMotionTouch</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="6"/>
+ <source>Configure Motion / Touch</source>
+ <translation>体感/触摸设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="20"/>
+ <source>Motion</source>
+ <translation>体感</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="28"/>
+ <source>Motion Provider:</source>
+ <translation>体感设备:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="42"/>
+ <source>Sensitivity:</source>
+ <translation>灵敏度:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="76"/>
+ <source>Touch</source>
+ <translation>触摸</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="84"/>
+ <source>Touch Provider:</source>
+ <translation>触摸设备:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="98"/>
+ <source>Calibration:</source>
+ <translation>触摸校准:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="105"/>
+ <source>(100, 50) - (1800, 850)</source>
+ <translation>(100, 50) - (1800, 850)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="121"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="154"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="227"/>
+ <source>Configure</source>
+ <translation>设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="138"/>
+ <source>Use button mapping:</source>
+ <translation>使用按键映射:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="166"/>
+ <source>CemuhookUDP Config</source>
+ <translation>CemuhookUDP 设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="172"/>
+ <source>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</source>
+ <translation>您可以使用任何与 Cemuhook 兼容的 UDP 输入源来提供体感和触摸输入。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="187"/>
+ <source>Server:</source>
+ <translation>服务器:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="208"/>
+ <source>Port:</source>
+ <translation>端口:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="229"/>
+ <source>Pad:</source>
+ <translation>Pad:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="237"/>
+ <source>Pad 1</source>
+ <translation>Pad 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="242"/>
+ <source>Pad 2</source>
+ <translation>Pad 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="247"/>
+ <source>Pad 3</source>
+ <translation>Pad 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="252"/>
+ <source>Pad 4</source>
+ <translation>Pad 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="264"/>
+ <source>Learn More</source>
+ <translation>了解更多</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.ui" line="277"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="250"/>
+ <source>Test</source>
+ <translation>测试</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="78"/>
+ <source>Mouse (Right Click)</source>
+ <translation>鼠标 (右键)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="79"/>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="84"/>
+ <source>CemuhookUDP</source>
+ <translation>CemuhookUDP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="83"/>
+ <source>Emulator Window</source>
+ <translation>模拟器窗口</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="101"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn More&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/using-a-controller-or-android-phone-for-motion-or-touch-input&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;了解更多&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="192"/>
+ <source>Testing</source>
+ <translation>测试中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="209"/>
+ <source>Configuring</source>
+ <translation>配置中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="241"/>
+ <source>Test Successful</source>
+ <translation>测试成功</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="242"/>
+ <source>Successfully received data from the server.</source>
+ <translation>已成功地从服务器获取数据。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="244"/>
+ <source>Test Failed</source>
+ <translation>测试失败</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="245"/>
+ <source>Could not receive valid data from the server.&lt;br&gt;Please verify that the server is set up correctly and the address and port are correct.</source>
+ <translation>无法从服务器获取数据。&lt;br&gt;请验证服务器是否正在运行,以及地址和端口是否配置正确。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="272"/>
+ <source>Citra</source>
+ <translation>Citra</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_motion_touch.cpp" line="273"/>
+ <source>UDP Test or calibration configuration is in progress.&lt;br&gt;Please wait for them to finish.</source>
+ <translation>UDP 测试或触摸校准正在进行中。&lt;br&gt;请耐心等待。</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureMouseAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="14"/>
+ <source>Configure Mouse</source>
+ <translation>鼠标设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="25"/>
+ <source>Mouse Buttons</source>
+ <translation>鼠标按键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="35"/>
+ <source>Forward:</source>
+ <translation>前:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="75"/>
+ <source>Back:</source>
+ <translation>后:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="103"/>
+ <source>Left:</source>
+ <translation>左:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="131"/>
+ <source>Middle:</source>
+ <translation>中:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="197"/>
+ <source>Right:</source>
+ <translation>右:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="270"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="109"/>
+ <source>Clear</source>
+ <translation>清除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.ui" line="289"/>
+ <source>Defaults</source>
+ <translation>系统默认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="111"/>
+ <source>[not set]</source>
+ <translation>[未设置]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="113"/>
+ <source>Restore Default</source>
+ <translation>恢复默认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="200"/>
+ <source>[press key]</source>
+ <translation>[请按一个键]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGame</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="14"/>
+ <source>Dialog</source>
+ <translation>对话框</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="28"/>
+ <source>Info</source>
+ <translation>信息</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="87"/>
+ <source>Name</source>
+ <translation>名称</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="94"/>
+ <source>Title ID</source>
+ <translation>游戏 ID</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="131"/>
+ <source>Filename</source>
+ <translation>文件名</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="158"/>
+ <source>Format</source>
+ <translation>格式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="165"/>
+ <source>Version</source>
+ <translation>版本</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="172"/>
+ <source>Size</source>
+ <translation>大小</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="179"/>
+ <source>Developer</source>
+ <translation>开发商</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="225"/>
+ <source>Add-Ons</source>
+ <translation>附加项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="230"/>
+ <source>General</source>
+ <translation>通用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="235"/>
+ <source>System</source>
+ <translation>系统</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="240"/>
+ <source>Graphics</source>
+ <translation>图形</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="245"/>
+ <source>Adv. Graphics</source>
+ <translation>高级图形</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.ui" line="250"/>
+ <source>Audio</source>
+ <translation>声音</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game.cpp" line="38"/>
+ <source>Properties</source>
+ <translation>属性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configuration_shared.cpp" line="131"/>
+ <source>Use global configuration (%1)</source>
+ <translation>使用全局设置 (%1)</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigurePerGameAddons</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="48"/>
+ <source>Patch Name</source>
+ <translation>补丁名称</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_per_game_addons.cpp" line="49"/>
+ <source>Version</source>
+ <translation>版本</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureProfileManager</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="22"/>
+ <source>Profile Manager</source>
+ <translation>用户配置管理</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="39"/>
+ <source>Current User</source>
+ <translation>当前用户</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="77"/>
+ <source>Username</source>
+ <translation>用户名</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="107"/>
+ <source>Set Image</source>
+ <translation>设置图像</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="127"/>
+ <source>Add</source>
+ <translation>添加</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="137"/>
+ <source>Rename</source>
+ <translation>重命名</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="147"/>
+ <source>Remove</source>
+ <translation>移除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.ui" line="159"/>
+ <source>Profile management is available only when game is not running.</source>
+ <translation>只有当游戏不在运行时,才能进行用户配置的管理。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="54"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="72"/>
+ <source>Enter Username</source>
+ <translation>输入用户名</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="135"/>
+ <source>Users</source>
+ <translation>用户</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="199"/>
+ <source>Enter a username for the new user:</source>
+ <translation>输入新用户的用户名:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="219"/>
+ <source>Enter a new username:</source>
+ <translation>输入新的用户名:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="244"/>
+ <source>Confirm Delete</source>
+ <translation>确认删除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="245"/>
+ <source>You are about to delete user with name &quot;%1&quot;. Are you sure?</source>
+ <translation>您确定要删除用户名为 “%1” 的用户吗?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="269"/>
+ <source>Select User Image</source>
+ <translation>选择用户图像</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="270"/>
+ <source>JPEG Images (*.jpg *.jpeg)</source>
+ <translation>JPEG 图像 (*.jpg *.jpeg)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="279"/>
+ <source>Error deleting image</source>
+ <translation>删除图像时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="280"/>
+ <source>Error occurred attempting to overwrite previous image at: %1.</source>
+ <translation>尝试覆盖该用户的现有图像时出错: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="288"/>
+ <source>Error deleting file</source>
+ <translation>删除文件时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="289"/>
+ <source>Unable to delete existing file: %1.</source>
+ <translation>无法删除文件: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="296"/>
+ <source>Error creating user image directory</source>
+ <translation>创建用户图像目录时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="297"/>
+ <source>Unable to create directory %1 for storing user images.</source>
+ <translation>无法创建存储用户图像的目录 %1 。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="302"/>
+ <source>Error copying user image</source>
+ <translation>复制用户图像时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_profile_manager.cpp" line="303"/>
+ <source>Unable to copy image from %1 to %2</source>
+ <translation>无法将图像从 %1 复制到 %2</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureService</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="22"/>
+ <source>BCAT</source>
+ <translation>BCAT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="34"/>
+ <source>BCAT is Nintendo&apos;s way of sending data to games to engage its community and unlock additional content.</source>
+ <translation>BCAT 是任天堂向游戏发送数据的一种方式,以此吸引游戏玩家,并可能解锁额外的内容。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="50"/>
+ <source>BCAT Backend</source>
+ <translation>BCAT 后端</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.ui" line="79"/>
+ <source>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Learn more about BCAT, Boxcat, and Current Events&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</source>
+ <translation>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;https://yuzu-emu.org/help/feature/boxcat&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;了解关于 BCAT、Boxcat 和当前事件的更多信息&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="81"/>
+ <source>The boxcat service is offline or you are not connected to the internet.</source>
+ <translation>Boxcat 服务处于离线状态,或者您没有连接到互联网。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="84"/>
+ <source>There was an error while processing the boxcat event data. Contact the yuzu developers.</source>
+ <translation>处理 Boxcat 事件数据时出错。请联系 yuzu 开发者。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="88"/>
+ <source>The version of yuzu you are using is either too new or too old for the server. Try updating to the latest official release of yuzu.</source>
+ <translation>您使用的 yuzu 版本过新或过旧。尝试更新到官方的最新版本。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="94"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>There are currently no events on boxcat.</source>
+ <translation>当前 Boxcat 上没有事件。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="109"/>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="111"/>
+ <source>Current Boxcat Events</source>
+ <translation>当前 Boxcat 事件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_service.cpp" line="121"/>
+ <source>Yuzu is retrieving the latest boxcat status...</source>
+ <translation>Yuzu 正在检索 Boxcat 的最新状态...</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureSystem</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="22"/>
+ <source>System Settings</source>
+ <translation>系统设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="30"/>
+ <source>Region:</source>
+ <translation>地区:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="38"/>
+ <source>Auto</source>
+ <translation>自动</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="43"/>
+ <source>Default</source>
+ <translation>系统默认</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="48"/>
+ <source>CET</source>
+ <translation>欧洲中部时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="53"/>
+ <source>CST6CDT</source>
+ <translation>古巴标准时间&amp;古巴夏令时</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="58"/>
+ <source>Cuba</source>
+ <translation>古巴</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="63"/>
+ <source>EET</source>
+ <translation>东欧时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="68"/>
+ <source>Egypt</source>
+ <translation>埃及</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="73"/>
+ <source>Eire</source>
+ <translation>爱尔兰</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="78"/>
+ <source>EST</source>
+ <translation>东部标准时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="83"/>
+ <source>EST5EDT</source>
+ <translation>东部标准时间&amp;东部夏令时</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="88"/>
+ <source>GB</source>
+ <translation>国家标准时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="93"/>
+ <source>GB-Eire</source>
+ <translation>爱尔兰国家标准时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="98"/>
+ <source>GMT</source>
+ <translation>格林威治标准时间 (GMT)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="103"/>
+ <source>GMT+0</source>
+ <translation>GMT+0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="108"/>
+ <source>GMT-0</source>
+ <translation>GMT-0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="113"/>
+ <source>GMT0</source>
+ <translation>GMT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="118"/>
+ <source>Greenwich</source>
+ <translation>格林威治</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="123"/>
+ <source>Hongkong</source>
+ <translation>中国香港</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="128"/>
+ <source>HST</source>
+ <translation>美国夏威夷时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="133"/>
+ <source>Iceland</source>
+ <translation>冰岛</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="138"/>
+ <source>Iran</source>
+ <translation>伊朗</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="143"/>
+ <source>Israel</source>
+ <translation>以色列</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="148"/>
+ <source>Jamaica</source>
+ <translation>牙买加</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="153"/>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="272"/>
+ <source>Japan</source>
+ <translation>日本</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="158"/>
+ <source>Kwajalein</source>
+ <translation>夸贾林环礁</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="163"/>
+ <source>Libya</source>
+ <translation>利比亚</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="168"/>
+ <source>MET</source>
+ <translation>中欧时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="173"/>
+ <source>MST</source>
+ <translation>山区标准时间 (北美)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="178"/>
+ <source>MST7MDT</source>
+ <translation>山区标准时间&amp;山区夏令时 (北美)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="183"/>
+ <source>Navajo</source>
+ <translation>纳瓦霍</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="188"/>
+ <source>NZ</source>
+ <translation>新西兰时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="193"/>
+ <source>NZ-CHAT</source>
+ <translation>新西兰-查塔姆群岛</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="198"/>
+ <source>Poland</source>
+ <translation>波兰</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="203"/>
+ <source>Portugal</source>
+ <translation>葡萄牙</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="208"/>
+ <source>PRC</source>
+ <translation>中国标准时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="213"/>
+ <source>PST8PDT</source>
+ <translation>太平洋标准时间&amp;太平洋夏令时</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="218"/>
+ <source>ROC</source>
+ <translation>ROC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="223"/>
+ <source>ROK</source>
+ <translation>ROK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="228"/>
+ <source>Singapore</source>
+ <translation>新加坡</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="233"/>
+ <source>Turkey</source>
+ <translation>土耳其</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="238"/>
+ <source>UCT</source>
+ <translation>UCT</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="243"/>
+ <source>Universal</source>
+ <translation>世界时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="248"/>
+ <source>UTC</source>
+ <translation>协调世界时</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="253"/>
+ <source>W-SU</source>
+ <translation>欧洲-莫斯科时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="258"/>
+ <source>WET</source>
+ <translation>西欧时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="263"/>
+ <source>Zulu</source>
+ <translation>祖鲁</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="277"/>
+ <source>USA</source>
+ <translation>美国</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="282"/>
+ <source>Europe</source>
+ <translation>欧洲</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="287"/>
+ <source>Australia</source>
+ <translation>澳大利亚</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="292"/>
+ <source>China</source>
+ <translation>中国</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="297"/>
+ <source>Korea</source>
+ <translation>朝鲜</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="302"/>
+ <source>Taiwan</source>
+ <translation>中国台湾</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="310"/>
+ <source>Time Zone:</source>
+ <translation>时区:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="317"/>
+ <source>Note: this can be overridden when region setting is auto-select</source>
+ <translation>注意:当“地区”设置是“自动选择”时,此设置可能会被覆盖。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="321"/>
+ <source>Japanese (日本語)</source>
+ <translation>日语 (日本語)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="326"/>
+ <source>English</source>
+ <translation>英语</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="331"/>
+ <source>French (français)</source>
+ <translation>法语 (français)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="336"/>
+ <source>German (Deutsch)</source>
+ <translation>德语 (Deutsch)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="341"/>
+ <source>Italian (italiano)</source>
+ <translation>意大利语 (italiano)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="346"/>
+ <source>Spanish (español)</source>
+ <translation>西班牙语 (español)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="351"/>
+ <source>Chinese</source>
+ <translation>中文</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="356"/>
+ <source>Korean (한국어)</source>
+ <translation>韩语 (한국어)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="361"/>
+ <source>Dutch (Nederlands)</source>
+ <translation>荷兰语 (Nederlands)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="366"/>
+ <source>Portuguese (português)</source>
+ <translation>葡萄牙语 (português)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="371"/>
+ <source>Russian (Русский)</source>
+ <translation>俄语 (Русский)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="376"/>
+ <source>Taiwanese</source>
+ <translation>台湾中文</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="381"/>
+ <source>British English</source>
+ <translation>英式英语</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="386"/>
+ <source>Canadian French</source>
+ <translation>加拿大法语</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="391"/>
+ <source>Latin American Spanish</source>
+ <translation>拉美西班牙语</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="396"/>
+ <source>Simplified Chinese</source>
+ <translation>简体中文</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="401"/>
+ <source>Traditional Chinese (正體中文)</source>
+ <translation>繁体中文 (正體中文)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="409"/>
+ <source>Custom RTC</source>
+ <translation>自定义系统时间</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="416"/>
+ <source>Language</source>
+ <translation>语言</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="423"/>
+ <source>RNG Seed</source>
+ <translation>随机数生成器种子</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="431"/>
+ <source>Mono</source>
+ <translation>单声道</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="436"/>
+ <source>Stereo</source>
+ <translation>立体声</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="441"/>
+ <source>Surround</source>
+ <translation>环绕声</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="449"/>
+ <source>Console ID:</source>
+ <translation>设备 ID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="456"/>
+ <source>Sound output mode</source>
+ <translation>声音输出模式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="470"/>
+ <source>d MMM yyyy h:mm:ss AP</source>
+ <translation>d MMM yyyy h:mm:ss AP</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="507"/>
+ <source>Regenerate</source>
+ <translation>重置 ID</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.ui" line="532"/>
+ <source>System settings are available only when game is not running.</source>
+ <translation>只有当游戏不在运行时,系统设置才可用。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="197"/>
+ <source>This will replace your current virtual Switch with a new one. Your current virtual Switch will not be recoverable. This might have unexpected effects in games. This might fail, if you use an outdated config savegame. Continue?</source>
+ <translation>这将使用一个新的虚拟 Switch 取代你当前的虚拟 Switch。您当前的虚拟 Switch 将无法恢复。在部分游戏中可能会出现意外效果。如果你使用一个过时的配置存档这可能会失败。确定要继续吗?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="201"/>
+ <source>Warning</source>
+ <translation>警告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_system.cpp" line="209"/>
+ <source>Console ID: 0x%1</source>
+ <translation>设备 ID: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchFromButton</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="14"/>
+ <source>Configure Touchscreen Mappings</source>
+ <translation>配置触摸屏映射</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="22"/>
+ <source>Mapping:</source>
+ <translation>映射:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="48"/>
+ <source>New</source>
+ <translation>新建</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="61"/>
+ <source>Delete</source>
+ <translation>删除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="74"/>
+ <source>Rename</source>
+ <translation>重命名</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="92"/>
+ <source>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</source>
+ <translation>单击屏幕底部区域添加点位,然后按下按键进行绑定。
+拖动点位以改变位置,或双击列表项更改绑定。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.ui" line="116"/>
+ <source>Delete Point</source>
+ <translation>删除点位</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Button</source>
+ <translation>按键</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>X</source>
+ <comment>X axis</comment>
+ <translation>X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="80"/>
+ <source>Y</source>
+ <comment>Y axis</comment>
+ <translation>Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>New Profile</source>
+ <translation>保存自定义设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="200"/>
+ <source>Enter the name for the new profile.</source>
+ <translation>为新的自定义设置命名。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete Profile</source>
+ <translation>删除自定义设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="211"/>
+ <source>Delete profile %1?</source>
+ <translation>真的要删除自定义设置 %1 吗?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>Rename Profile</source>
+ <translation>重命名自定义设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="224"/>
+ <source>New name:</source>
+ <translation>新名称:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="233"/>
+ <source>[press key]</source>
+ <translation>[请按一个键]</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureTouchscreenAdvanced</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="14"/>
+ <source>Configure Touchscreen</source>
+ <translation>触摸屏设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="26"/>
+ <source>Warning: The settings in this page affect the inner workings of yuzu&apos;s emulated touchscreen. Changing them may result in undesirable behavior, such as the touchscreen partially or not working. You should only use this page if you know what you are doing.</source>
+ <translation>警告:此页面中的设置会影响 yuzu 的模拟触摸屏的内部工作方式。改变它们可能造成不良后果,例如触摸屏完全或部分失效。如果您不知道自己在做什么,请不要使用此页面。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="52"/>
+ <source>Touch Parameters</source>
+ <translation>触摸参数</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="71"/>
+ <source>Touch Diameter Y</source>
+ <translation>触摸直径 Y</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="78"/>
+ <source>Finger</source>
+ <translation>手指</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="98"/>
+ <source>Touch Diameter X</source>
+ <translation>触摸直径 X</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="115"/>
+ <source>Rotational Angle</source>
+ <translation>旋转角度</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touchscreen_advanced.ui" line="149"/>
+ <source>Restore Defaults</source>
+ <translation>恢复默认</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureUi</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="14"/>
+ <source>Form</source>
+ <translation>类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="20"/>
+ <source>General</source>
+ <translation>通用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="28"/>
+ <source>Note: Changing language will apply your configuration.</source>
+ <translation>注意: 切换语言将应用您的配置。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="40"/>
+ <source>Interface language:</source>
+ <translation>界面语言:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="54"/>
+ <source>Theme:</source>
+ <translation>主题:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="71"/>
+ <source>Game List</source>
+ <translation>游戏列表</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="79"/>
+ <source>Show Add-Ons Column</source>
+ <translation>显示“附加项”列</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="88"/>
+ <source>Icon Size:</source>
+ <translation>图标大小:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="102"/>
+ <source>Row 1 Text:</source>
+ <translation>第一行:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="116"/>
+ <source>Row 2 Text:</source>
+ <translation>第二行:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="133"/>
+ <source>Screenshots</source>
+ <translation>捕获截图</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="141"/>
+ <source>Ask Where To Save Screenshots (Windows Only)</source>
+ <translation>询问保存截图的位置 (仅限 Windows )</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="150"/>
+ <source>Screenshots Path: </source>
+ <translation>截图保存位置:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.ui" line="160"/>
+ <source>...</source>
+ <translation>...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="64"/>
+ <source>Select Screenshots Path...</source>
+ <translation>选择截图保存位置...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="131"/>
+ <source>&lt;System&gt;</source>
+ <translation>&lt;System&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_ui.cpp" line="132"/>
+ <source>English</source>
+ <translation>英语</translation>
+ </message>
+</context>
+<context>
+ <name>ConfigureWeb</name>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="14"/>
+ <source>Form</source>
+ <translation>Form</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="22"/>
+ <source>yuzu Web Service</source>
+ <translation>yuzu 网络服务</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="28"/>
+ <source>By providing your username and token, you agree to allow yuzu to collect additional usage data, which may include user identifying information.</source>
+ <translation>提供您的用户名和令牌意味着您同意让 yuzu 收集额外的使用数据,其中可能包括用户识别信息。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="155"/>
+ <source>Verify</source>
+ <translation>验证</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="53"/>
+ <source>Sign up</source>
+ <translation>注册</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="63"/>
+ <source>Token: </source>
+ <translation>令牌:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="74"/>
+ <source>Username: </source>
+ <translation>用户名:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="91"/>
+ <source>What is my token?</source>
+ <translation>我的令牌是?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="116"/>
+ <source>Telemetry</source>
+ <translation>使用数据共享</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="122"/>
+ <source>Share anonymous usage data with the yuzu team</source>
+ <translation>与 yuzu 团队共享匿名使用数据</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="129"/>
+ <source>Learn more</source>
+ <translation>了解更多</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="138"/>
+ <source>Telemetry ID:</source>
+ <translation>数据 ID:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="154"/>
+ <source>Regenerate</source>
+ <translation>重新生成</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="168"/>
+ <source>Discord Presence</source>
+ <translation>Discord 状态</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.ui" line="174"/>
+ <source>Show Current Game in your Discord Status</source>
+ <translation>在您的 Discord 状态中显示当前游戏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="69"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Learn more&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;了解更多&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="73"/>
+ <source>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;Sign up&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://profile.yuzu-emu.org/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;注册&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="77"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;What is my token?&lt;/span&gt;&lt;/a&gt;</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/wiki/yuzu-web-service/&apos;&gt;&lt;span style=&quot;text-decoration: underline; color:#039be5;&quot;&gt;我的令牌是?&lt;/span&gt;&lt;/a&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="81"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="126"/>
+ <source>Telemetry ID: 0x%1</source>
+ <translation>数据 ID: 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="92"/>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="166"/>
+ <source>Unspecified</source>
+ <translation>未指定</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="118"/>
+ <source>Token not verified</source>
+ <translation>令牌未被验证</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="119"/>
+ <source>Token was not verified. The change to your token has not been saved.</source>
+ <translation>令牌未被验证。您对用户名和令牌的更改尚未保存。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="145"/>
+ <source>Verifying...</source>
+ <translation>验证中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="167"/>
+ <source>Verification failed</source>
+ <translation>验证失败</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_web.cpp" line="168"/>
+ <source>Verification failed. Check that you have entered your token correctly, and that your internet connection is working.</source>
+ <translation>验证失败。请检查您输入的令牌是否正确,并且确保您的互联网连接正常。</translation>
+ </message>
+</context>
+<context>
+ <name>GMainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="165"/>
+ <source>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;Anonymous data is collected&lt;/a&gt; to help improve yuzu. &lt;br/&gt;&lt;br/&gt;Would you like to share your usage data with us?</source>
+ <translation>&lt;a href=&apos;https://yuzu-emu.org/help/feature/telemetry/&apos;&gt;我们收集匿名数据&lt;/a&gt;来帮助改进 yuzu 。&lt;br/&gt;&lt;br/&gt;您愿意和我们分享您的使用数据吗?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="168"/>
+ <source>Telemetry</source>
+ <translation>使用数据共享</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="326"/>
+ <source>Text Check Failed</source>
+ <translation>文本检查失败</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="339"/>
+ <source>Loading Web Applet...</source>
+ <translation>正在加载 Web 应用程序...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="387"/>
+ <source>Exit Web Applet</source>
+ <translation>退出 Web 应用程序</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="405"/>
+ <source>Exit</source>
+ <translation>退出</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="406"/>
+ <source>To exit the web application, use the game provided controls to select exit, select the &apos;Exit Web Applet&apos; option in the menu bar, or press the &apos;Enter&apos; key.</source>
+ <translation>若要退出 Web 应用程序,请使用游戏内提供的方式退出,在菜单栏中选择“退出 Web 应用程序”选项,或者按 &quot;Enter&quot; 键。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="458"/>
+ <source>Web Applet</source>
+ <translation>Web 应用程序</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="459"/>
+ <source>This version of yuzu was built without QtWebEngine support, meaning that yuzu cannot properly display the game manual or web page requested.</source>
+ <translation>此版本的 yuzu 没有 QtWebEngine 的支持。这意味着 yuzu 无法正确显示所请求的游戏手册或网页。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="507"/>
+ <source>The amount of shaders currently being built</source>
+ <translation>当前正在构建的着色器的数量</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="510"/>
+ <source>Current emulation speed. Values higher or lower than 100% indicate emulation is running faster or slower than a Switch.</source>
+ <translation>当前的模拟速度。高于或低于 100% 的值表示模拟正在运行得比实际 Switch 更快或更慢。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="513"/>
+ <source>How many frames per second the game is currently displaying. This will vary from game to game and scene to scene.</source>
+ <translation>游戏当前运行的帧率。这将因游戏和场景的不同而有所变化。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="517"/>
+ <source>Time taken to emulate a Switch frame, not counting framelimiting or v-sync. For full-speed emulation this should be at most 16.67 ms.</source>
+ <translation>在不计算速度限制和垂直同步的情况下,模拟一个 Switch 帧的实际时间。若要进行全速模拟,这个数值不应超过 16.67 毫秒。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="537"/>
+ <source>DOCK</source>
+ <translation>DOCK</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="556"/>
+ <source>ASYNC</source>
+ <translation>异步</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="576"/>
+ <source>MULTICORE</source>
+ <translation>多核</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>VULKAN</source>
+ <translation>VULKAN</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="588"/>
+ <source>OPENGL</source>
+ <translation>OPENGL</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="647"/>
+ <source>Clear Recent Files</source>
+ <translation>清除最近文件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="990"/>
+ <source>Warning Outdated Game Format</source>
+ <translation>过时游戏格式警告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="991"/>
+ <source>You are using the deconstructed ROM directory format for this game, which is an outdated format that has been superseded by others such as NCA, NAX, XCI, or NSP. Deconstructed ROM directories lack icons, metadata, and update support.&lt;br&gt;&lt;br&gt;For an explanation of the various Switch formats yuzu supports, &lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;check out our wiki&lt;/a&gt;. This message will not be shown again.</source>
+ <translation>目前使用的游戏为解体的 ROM 目录格式,这是一种过时的格式,已被其他格式替代,如 NCA,NAX,XCI 或 NSP。解体的 ROM 目录缺少图标、元数据和更新支持。&lt;br&gt;&lt;br&gt;有关 yuzu 支持的各种 Switch 格式的说明,&lt;a href=&apos;https://yuzu-emu.org/wiki/overview-of-switch-game-formats&apos;&gt;请查看我们的 wiki&lt;/a&gt;。此消息将不会再次出现。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1003"/>
+ <location filename="../../src/yuzu/main.cpp" line="1036"/>
+ <source>Error while loading ROM!</source>
+ <translation>加载 ROM 时出错!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1004"/>
+ <source>The ROM format is not supported.</source>
+ <translation>该 ROM 格式不受支持。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1008"/>
+ <source>An error occurred initializing the video core.</source>
+ <translation>在初始化视频核心时发生错误。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1009"/>
+ <source>yuzu has encountered an error while running the video core, please see the log for more details.For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.Ensure that you have the latest graphics drivers for your GPU.</source>
+ <translation>yuzu 在运行视频核心时遇到错误,请查看日志了解更多详细信息。有关查看日志的更多信息,请参阅以下页面:&lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;。请确保您的 GPU 安装了最新的图形驱动程序。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1028"/>
+ <source>Error while loading ROM! </source>
+ <translation>加载 ROM 时出错!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1037"/>
+ <source>An unknown error occurred. Please see the log for more details.</source>
+ <translation>发生了未知错误。请查看日志了解详情。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1169"/>
+ <source>Start</source>
+ <translation>开始</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1274"/>
+ <source>Save Data</source>
+ <translation>保存数据</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1318"/>
+ <source>Mod Data</source>
+ <translation>Mod 数据</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1330"/>
+ <source>Error Opening %1 Folder</source>
+ <translation>打开 %1 文件夹时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1331"/>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Folder does not exist!</source>
+ <translation>文件夹不存在!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1349"/>
+ <source>Error Opening Transferable Shader Cache</source>
+ <translation>打开可转移着色器缓存时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1350"/>
+ <location filename="../../src/yuzu/main.cpp" line="1544"/>
+ <source>A shader cache for this title does not exist.</source>
+ <translation>这个游戏的着色器缓存不存在。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1414"/>
+ <source>Contents</source>
+ <translation>目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1416"/>
+ <source>Update</source>
+ <translation>游戏更新</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1418"/>
+ <source>DLC</source>
+ <translation>DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Entry</source>
+ <translation>删除项目</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1425"/>
+ <source>Remove Installed Game %1?</source>
+ <translation>删除已安装的游戏 %1 ?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1455"/>
+ <location filename="../../src/yuzu/main.cpp" line="1471"/>
+ <location filename="../../src/yuzu/main.cpp" line="1502"/>
+ <location filename="../../src/yuzu/main.cpp" line="1549"/>
+ <location filename="../../src/yuzu/main.cpp" line="1570"/>
+ <source>Successfully Removed</source>
+ <translation>删除成功</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1456"/>
+ <source>Successfully removed the installed base game.</source>
+ <translation>成功删除已安装的游戏。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1459"/>
+ <location filename="../../src/yuzu/main.cpp" line="1474"/>
+ <location filename="../../src/yuzu/main.cpp" line="1497"/>
+ <source>Error Removing %1</source>
+ <translation>删除 %1 时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1460"/>
+ <source>The base game is not installed in the NAND and cannot be removed.</source>
+ <translation>该游戏未安装于 NAND 中,无法删除。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1472"/>
+ <source>Successfully removed the installed update.</source>
+ <translation>成功删除已安装的游戏更新。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1475"/>
+ <source>There is no update installed for this title.</source>
+ <translation>这个游戏没有任何已安装的更新。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1498"/>
+ <source>There are no DLC installed for this title.</source>
+ <translation>这个游戏没有任何已安装的 DLC 。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1503"/>
+ <source>Successfully removed %1 installed DLC.</source>
+ <translation>成功删除游戏 %1 安装的 DLC 。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1510"/>
+ <source>Delete Transferable Shader Cache?</source>
+ <translation>删除着色器缓存?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1512"/>
+ <source>Remove Custom Game Configuration?</source>
+ <translation>移除自定义游戏设置?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1518"/>
+ <source>Remove File</source>
+ <translation>删除文件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1543"/>
+ <location filename="../../src/yuzu/main.cpp" line="1552"/>
+ <source>Error Removing Transferable Shader Cache</source>
+ <translation>删除着色器缓存时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1550"/>
+ <source>Successfully removed the transferable shader cache.</source>
+ <translation>成功删除着色器缓存。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1553"/>
+ <source>Failed to remove the transferable shader cache.</source>
+ <translation>删除着色器缓存失败。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1564"/>
+ <location filename="../../src/yuzu/main.cpp" line="1573"/>
+ <source>Error Removing Custom Configuration</source>
+ <translation>移除自定义游戏设置时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1565"/>
+ <source>A custom configuration for this title does not exist.</source>
+ <translation>这个游戏的自定义设置不存在。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1571"/>
+ <source>Successfully removed the custom game configuration.</source>
+ <translation>成功移除自定义游戏设置。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1574"/>
+ <source>Failed to remove the custom game configuration.</source>
+ <translation>移除自定义游戏设置失败。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1580"/>
+ <source>RomFS Extraction Failed!</source>
+ <translation>RomFS 提取失败!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1581"/>
+ <source>There was an error copying the RomFS files or the user cancelled the operation.</source>
+ <translation>复制 RomFS 文件时出错,或用户取消了操作。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Full</source>
+ <translation>完整</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1633"/>
+ <source>Skeleton</source>
+ <translation>框架</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1635"/>
+ <source>Select RomFS Dump Mode</source>
+ <translation>选择 RomFS 转储模式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1636"/>
+ <source>Please select the how you would like the RomFS dumped.&lt;br&gt;Full will copy all of the files into the new directory while &lt;br&gt;skeleton will only create the directory structure.</source>
+ <translation>请选择希望 RomFS 转储的方式。&lt;br&gt;“Full” 会将所有文件复制到新目录中,而&lt;br&gt;“Skeleton” 只会创建目录结构。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <source>Extracting RomFS...</source>
+ <translation>正在提取 RomFS...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1649"/>
+ <location filename="../../src/yuzu/main.cpp" line="1822"/>
+ <source>Cancel</source>
+ <translation>取消</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1656"/>
+ <source>RomFS Extraction Succeeded!</source>
+ <translation>RomFS 提取成功!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1657"/>
+ <source>The operation completed successfully.</source>
+ <translation>操作成功完成。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1698"/>
+ <source>Error Opening %1</source>
+ <translation>打开 %1 时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1705"/>
+ <source>Select Directory</source>
+ <translation>选择目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1731"/>
+ <source>Properties</source>
+ <translation>属性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1732"/>
+ <source>The game properties could not be loaded.</source>
+ <translation>无法加载该游戏的属性信息。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1744"/>
+ <source>Switch Executable (%1);;All Files (*.*)</source>
+ <comment>%1 is an identifier for the Switch executable file extensions.</comment>
+ <translation>Switch 可执行文件 (%1);;所有文件 (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1748"/>
+ <source>Load File</source>
+ <translation>加载文件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1760"/>
+ <source>Open Extracted ROM Directory</source>
+ <translation>打开提取的 ROM 目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1771"/>
+ <source>Invalid Directory Selected</source>
+ <translation>选择的目录无效</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1772"/>
+ <source>The directory you have selected does not contain a &apos;main&apos; file.</source>
+ <translation>选择的目录不包含 “main” 文件。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1782"/>
+ <source>Installable Switch File (*.nca *.nsp *.xci);;Nintendo Content Archive (*.nca);;Nintendo Submission Package (*.nsp);;NX Cartridge Image (*.xci)</source>
+ <translation>可安装的 Switch 文件 (*.nca *.nsp *.xci);;任天堂内容档案 (*.nca);;任天堂应用包 (*.nsp);;NX 卡带镜像 (*.xci)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1787"/>
+ <source>Install Files</source>
+ <translation>安装文件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1832"/>
+ <source>Installing file &quot;%1&quot;...</source>
+ <translation>正在安装文件 &quot;%1&quot;...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1880"/>
+ <source>Install Results</source>
+ <translation>安装结果</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1977"/>
+ <source>System Application</source>
+ <translation>系统应用</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1978"/>
+ <source>System Archive</source>
+ <translation>系统档案</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1979"/>
+ <source>System Application Update</source>
+ <translation>系统应用更新</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1980"/>
+ <source>Firmware Package (Type A)</source>
+ <translation>固件包 (A型)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1981"/>
+ <source>Firmware Package (Type B)</source>
+ <translation>固件包 (B型)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1982"/>
+ <source>Game</source>
+ <translation>游戏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1983"/>
+ <source>Game Update</source>
+ <translation>游戏更新</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1984"/>
+ <source>Game DLC</source>
+ <translation>游戏 DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1985"/>
+ <source>Delta Title</source>
+ <translation>差量程序</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1988"/>
+ <source>Select NCA Install Type...</source>
+ <translation>选择 NCA 安装类型...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1989"/>
+ <source>Please select the type of title you would like to install this NCA as:
+(In most instances, the default &apos;Game&apos; is fine.)</source>
+ <translation>请选择此 NCA 的程序类型:
+(在大多数情况下,选择默认的“游戏”即可。)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1995"/>
+ <source>Failed to Install</source>
+ <translation>安装失败</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="1996"/>
+ <source>The title type you selected for the NCA is invalid.</source>
+ <translation>选择的 NCA 程序类型无效。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2037"/>
+ <source>File not found</source>
+ <translation>找不到文件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2038"/>
+ <source>File &quot;%1&quot; not found</source>
+ <translation>文件 &quot;%1&quot; 未找到</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2060"/>
+ <location filename="../../src/yuzu/main.cpp" line="2867"/>
+ <source>Continue</source>
+ <translation>继续</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2101"/>
+ <source>Error Display</source>
+ <translation>错误显示</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2111"/>
+ <source>Missing yuzu Account</source>
+ <translation>未设置 yuzu 账户</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2112"/>
+ <source>In order to submit a game compatibility test case, you must link your yuzu account.&lt;br&gt;&lt;br/&gt;To link your yuzu account, go to Emulation &amp;gt; Configuration &amp;gt; Web.</source>
+ <translation>要提交游戏兼容性测试用例,您必须设置您的 yuzu 帐户。&lt;br&gt;&lt;br/&gt;要设置您的 yuzu 帐户,请转到模拟 &amp;gt; 设置 &amp;gt; 网络。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2122"/>
+ <source>Error opening URL</source>
+ <translation>打开 URL 时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2123"/>
+ <source>Unable to open the URL &quot;%1&quot;.</source>
+ <translation>无法打开 URL : &quot;%1&quot; 。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2287"/>
+ <source>Amiibo File (%1);; All Files (*.*)</source>
+ <translation>Amiibo 文件 (%1);; 全部文件 (*.*)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2288"/>
+ <source>Load Amiibo</source>
+ <translation>加载 Amiibo</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2307"/>
+ <source>Error opening Amiibo data file</source>
+ <translation>打开 Amiibo 数据文件时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2308"/>
+ <source>Unable to open Amiibo file &quot;%1&quot; for reading.</source>
+ <translation>无法打开 Amiibo 文件 %1。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2316"/>
+ <source>Error reading Amiibo data file</source>
+ <translation>读取 Amiibo 数据文件时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2317"/>
+ <source>Unable to fully read Amiibo data. Expected to read %1 bytes, but was only able to read %2 bytes.</source>
+ <translation>无法完全读取 Amiibo 数据。应读取 %1 个字节,但实际仅能读取 %2 个字节。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2325"/>
+ <source>Error loading Amiibo data</source>
+ <translation>加载 Amiibo 数据时出错</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2326"/>
+ <source>Unable to load Amiibo data.</source>
+ <translation>无法加载 Amiibo 数据。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2364"/>
+ <source>Capture Screenshot</source>
+ <translation>捕获截图</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2365"/>
+ <source>PNG Image (*.png)</source>
+ <translation>PNG 图像 (*.png)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2418"/>
+ <source>Speed: %1% / %2%</source>
+ <translation>速度: %1% / %2%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2422"/>
+ <source>Speed: %1%</source>
+ <translation>速度: %1%</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2424"/>
+ <source>Game: %1 FPS</source>
+ <translation>FPS: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2425"/>
+ <source>Frame: %1 ms</source>
+ <translation>帧延迟:%1 毫秒</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2473"/>
+ <source>The game you are trying to load requires additional files from your Switch to be dumped before playing.&lt;br/&gt;&lt;br/&gt;For more information on dumping these files, please see the following wiki page: &lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>您正尝试启动的游戏需要从 Switch 转储的其他文件。&lt;br/&gt;&lt;br/&gt;有关转储这些文件的更多信息,请参阅以下 wiki 页面:&lt;a href=&apos;https://yuzu-emu.org/wiki/dumping-system-archives-and-the-shared-fonts-from-a-switch-console/&apos;&gt;Dumping System Archives and the Shared Fonts from a Switch Console&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;您要退出并返回至游戏列表吗?继续模拟可能会导致崩溃,存档损坏或其他错误。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2488"/>
+ <source>yuzu was unable to locate a Switch system archive. %1</source>
+ <translation>Yuzu 找不到 Switch 系统档案 %1 </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2490"/>
+ <source>yuzu was unable to locate a Switch system archive: %1. %2</source>
+ <translation>Yuzu 找不到 Switch 系统档案: %1, %2 </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2494"/>
+ <source>System Archive Not Found</source>
+ <translation>未找到系统档案</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2496"/>
+ <source>System Archive Missing</source>
+ <translation>系统档案缺失</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2502"/>
+ <source>yuzu was unable to locate the Switch shared fonts. %1</source>
+ <translation>Yuzu 找不到 Swtich 共享字体 %1 </translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2503"/>
+ <source>Shared Fonts Not Found</source>
+ <translation>未找到共享字体</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2505"/>
+ <source>Shared Font Missing</source>
+ <translation>共享字体文件缺失</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2511"/>
+ <source>Fatal Error</source>
+ <translation>致命错误</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2512"/>
+ <source>yuzu has encountered a fatal error, please see the log for more details. For more information on accessing the log, please see the following page: &lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;.&lt;br/&gt;&lt;br/&gt;Would you like to quit back to the game list? Continuing emulation may result in crashes, corrupted save data, or other bugs.</source>
+ <translation>yuzu 遇到了致命错误,请查看日志了解详情。有关查找日志的更多信息,请参阅以下页面:&lt;a href=&apos;https://community.citra-emu.org/t/how-to-upload-the-log-file/296&apos;&gt;How to Upload the Log File&lt;/a&gt;。&lt;br/&gt;&lt;br/&gt;您要退出并返回至游戏列表吗?继续模拟可能会导致崩溃,存档损坏或其他错误。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2521"/>
+ <source>Fatal Error encountered</source>
+ <translation>发生致命错误</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2544"/>
+ <source>Confirm Key Rederivation</source>
+ <translation>确认重新生成密钥</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2545"/>
+ <source>You are about to force rederive all of your keys.
+If you do not know what this means or what you are doing,
+this is a potentially destructive action.
+Please make sure this is what you want
+and optionally make backups.
+
+This will delete your autogenerated key files and re-run the key derivation module.</source>
+ <translation>即将强制重新生成您的全部密钥。
+如果您不清楚这意味着什么,或您在做什么,
+这可能具有破坏性后果。
+请确保您希望这样做,并且做好备份。
+
+这将删除您自动生成的密钥文件并重新运行密钥生成模块。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2578"/>
+ <source>Missing fuses</source>
+ <translation>项目丢失</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2581"/>
+ <source> - Missing BOOT0</source>
+ <translation>- 丢失 BOOT0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2584"/>
+ <source> - Missing BCPKG2-1-Normal-Main</source>
+ <translation> - 丢失 BCPKG2-1-Normal-Main</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2587"/>
+ <source> - Missing PRODINFO</source>
+ <translation>- 丢失 PRODINFO</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2591"/>
+ <source>Derivation Components Missing</source>
+ <translation>组件丢失</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2592"/>
+ <source>Components are missing that may hinder key derivation from completing. &lt;br&gt;Please follow &lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;the yuzu quickstart guide&lt;/a&gt; to get all your keys and games.&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</source>
+ <translation>缺少组件可能会影响密钥的使用。&lt;br&gt;请查看&lt;a href=&apos;https://yuzu-emu.org/help/quickstart/&apos;&gt;yuzu 快速导航&lt;/a&gt;以获得你的密钥和游戏。&lt;br&gt;&lt;br&gt;&lt;small&gt;(%1)&lt;/small&gt;</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2601"/>
+ <source>Deriving keys...
+This may take up to a minute depending
+on your system&apos;s performance.</source>
+ <translation>正在生成密钥...
+这可能需要最多一分钟,具体取决于
+您的系统性能。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2603"/>
+ <source>Deriving Keys</source>
+ <translation>生成密钥</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2648"/>
+ <source>Select RomFS Dump Target</source>
+ <translation>选择 RomFS 转储目标</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2649"/>
+ <source>Please select which RomFS you would like to dump.</source>
+ <translation>请选择希望转储的 RomFS。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <location filename="../../src/yuzu/main.cpp" line="2756"/>
+ <location filename="../../src/yuzu/main.cpp" line="2767"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2665"/>
+ <source>Are you sure you want to close yuzu?</source>
+ <translation>您确定要关闭 yuzu 吗?</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2757"/>
+ <source>Are you sure you want to stop the emulation? Any unsaved progress will be lost.</source>
+ <translation>您确定要停止模拟吗?未保存的进度将会丢失。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.cpp" line="2768"/>
+ <source>The currently running application has requested yuzu to not exit.
+
+Would you like to bypass this and exit anyway?</source>
+ <translation>当前运行的应用程序请求 yuzu 不要退出。
+
+您希望忽略并退出吗?</translation>
+ </message>
+</context>
+<context>
+ <name>GRenderWindow</name>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="600"/>
+ <source>OpenGL not available!</source>
+ <translation>OpenGL 模式不可用!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="601"/>
+ <source>yuzu has not been compiled with OpenGL support.</source>
+ <translation>yuzu 没有使用 OpenGL 进行编译。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="615"/>
+ <source>Vulkan not available!</source>
+ <translation>Vulkan 模式不可用!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="616"/>
+ <source>yuzu has not been compiled with Vulkan support.</source>
+ <translation>yuzu 没有使用 Vulkan 进行编译。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="625"/>
+ <source>Error while initializing OpenGL 4.3!</source>
+ <translation>初始化 OpenGL 4.3 时出错!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="626"/>
+ <source>Your GPU may not support OpenGL 4.3, or you do not have the latest graphics driver.</source>
+ <translation>您的 GPU 可能不支持 OpenGL 4.3 ,或者您没有安装最新的显卡驱动。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="634"/>
+ <source>Error while initializing OpenGL!</source>
+ <translation>初始化 OpenGL 时出错!</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/bootmanager.cpp" line="635"/>
+ <source>Your GPU may not support one or more required OpenGL extensions. Please ensure you have the latest graphics driver.&lt;br&gt;&lt;br&gt;Unsupported extensions:&lt;br&gt;</source>
+ <translation>您的 GPU 可能不支持某些必需的 OpenGL 扩展。请确保您已经安装最新的显卡驱动。&lt;br&gt;&lt;br&gt;不支持的扩展:&lt;br&gt;</translation>
+ </message>
+</context>
+<context>
+ <name>GameList</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="307"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="651"/>
+ <source>Name</source>
+ <translation>名称</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="308"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="652"/>
+ <source>Compatibility</source>
+ <translation>兼容性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="311"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="655"/>
+ <source>Add-ons</source>
+ <translation>附加项</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="312"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="315"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="656"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="659"/>
+ <source>File type</source>
+ <translation>文件类型</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="313"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="316"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="657"/>
+ <location filename="../../src/yuzu/game_list.cpp" line="660"/>
+ <source>Size</source>
+ <translation>大小</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="476"/>
+ <source>Open Save Data Location</source>
+ <translation>打开存档位置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="477"/>
+ <source>Open Mod Data Location</source>
+ <translation>打开 MOD 数据位置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="479"/>
+ <source>Open Transferable Shader Cache</source>
+ <translation>打开可转移着色器缓存</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="481"/>
+ <source>Remove</source>
+ <translation>删除</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="482"/>
+ <source>Remove Installed Update</source>
+ <translation>删除已安装的游戏更新</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="483"/>
+ <source>Remove All Installed DLC</source>
+ <translation>删除所有已安装 DLC</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="484"/>
+ <source>Remove Shader Cache</source>
+ <translation>删除着色器缓存</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="485"/>
+ <source>Remove Custom Configuration</source>
+ <translation>删除自定义设置</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="487"/>
+ <source>Remove All Installed Contents</source>
+ <translation>删除所有安装的项目</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="488"/>
+ <source>Dump RomFS</source>
+ <translation>转储 RomFS</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="489"/>
+ <source>Copy Title ID to Clipboard</source>
+ <translation>复制游戏 ID 到剪贴板</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="490"/>
+ <source>Navigate to GameDB entry</source>
+ <translation>查看兼容性报告</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="492"/>
+ <source>Properties</source>
+ <translation>属性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="542"/>
+ <source>Scan Subfolders</source>
+ <translation>扫描子文件夹</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="543"/>
+ <source>Remove Game Directory</source>
+ <translation>移除游戏目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="562"/>
+ <source>▲ Move Up</source>
+ <translation>▲ 向上移动</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="563"/>
+ <source>▼ Move Down</source>
+ <translation>▼ 向下移动</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="564"/>
+ <source>Open Directory Location</source>
+ <translation>打开目录位置</translation>
+ </message>
+</context>
+<context>
+ <name>GameListItemCompat</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Perfect</source>
+ <translation>完美</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="146"/>
+ <source>Game functions flawless with no audio or graphical glitches, all tested functionality works as intended without
+any workarounds needed.</source>
+ <translation>游戏功能完美,没有音频或图形问题,所有测试功能按预期工作,无需需要任何解决办法。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Great</source>
+ <translation>良好</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="147"/>
+ <source>Game functions with minor graphical or audio glitches and is playable from start to finish. May require some
+workarounds.</source>
+ <translation>游戏运行时会有非常轻微的图像或音频问题,但是能从头玩到尾。可能需要一些技巧才能完成游戏。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Okay</source>
+ <translation>一般</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="148"/>
+ <source>Game functions with major graphical or audio glitches, but game is playable from start to finish with
+workarounds.</source>
+ <translation>游戏运行时会有很多图像或音频错误,但是在使用一些特殊技巧之后能完整地完成游戏。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Bad</source>
+ <translation>较差</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="149"/>
+ <source>Game functions, but with major graphical or audio glitches. Unable to progress in specific areas due to glitches
+even with workarounds.</source>
+ <translation>游戏能运行,但是会有大量图像或音频错误。即使使用一些技巧也无法通过游戏的某些区域。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Intro/Menu</source>
+ <translation>开场 / 菜单</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="150"/>
+ <source>Game is completely unplayable due to major graphical or audio glitches. Unable to progress past the Start
+Screen.</source>
+ <translation>游戏完全没法玩,图像或音频有重大错误。通过开场菜单后无法继续。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>Won&apos;t Boot</source>
+ <translation>无法打开</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="151"/>
+ <source>The game crashes when attempting to startup.</source>
+ <translation>在启动游戏时直接崩溃了。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>Not Tested</source>
+ <translation>未测试</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="152"/>
+ <source>The game has not yet been tested.</source>
+ <translation>游戏尚未经过测试。</translation>
+ </message>
+</context>
+<context>
+ <name>GameListPlaceholder</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="722"/>
+ <source>Double-click to add a new folder to the game list</source>
+ <translation>双击以添加新的游戏文件夹</translation>
+ </message>
+</context>
+<context>
+ <name>GameListSearchField</name>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="120"/>
+ <source>Filter:</source>
+ <translation>搜索:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list.cpp" line="123"/>
+ <source>Enter pattern to filter</source>
+ <translation>搜索游戏</translation>
+ </message>
+</context>
+<context>
+ <name>InstallDialog</name>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="31"/>
+ <source>Please confirm these are the files you wish to install.</source>
+ <translation>请确认这些您想要安装的文件。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="34"/>
+ <source>Installing an Update or DLC will overwrite the previously installed one.</source>
+ <translation>安装游戏更新或 DLC 时,会覆盖以前安装的内容。</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="38"/>
+ <source>Install</source>
+ <translation>安装</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/install_dialog.cpp" line="52"/>
+ <source>Install Files to NAND</source>
+ <translation>安装文件到 NAND</translation>
+ </message>
+</context>
+<context>
+ <name>LoadingScreen</name>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="84"/>
+ <source>Loading Shaders 387 / 1628</source>
+ <translation>正在加载着色器: 387 / 1628</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="121"/>
+ <source>Loading Shaders %v out of %m</source>
+ <translation>正在加载着色器: %v / %m</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.ui" line="135"/>
+ <source>Estimated Time 5m 4s</source>
+ <translation>所需时间: 5 分 4 秒</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="91"/>
+ <source>Loading...</source>
+ <translation>加载中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="92"/>
+ <source>Loading Shaders %1 / %2</source>
+ <translation>正在加载着色器: %1 / %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="93"/>
+ <source>Launching...</source>
+ <translation>启动中...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/loading_screen.cpp" line="174"/>
+ <source>Estimated Time %1</source>
+ <translation>所需时间: %1</translation>
+ </message>
+</context>
+<context>
+ <name>MainWindow</name>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="14"/>
+ <source>yuzu</source>
+ <translation>yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="53"/>
+ <source>&amp;File</source>
+ <translation>文件 (&amp;F)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="57"/>
+ <source>Recent Files</source>
+ <translation>最近文件</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="76"/>
+ <source>&amp;Emulation</source>
+ <translation>模拟 (&amp;E)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="88"/>
+ <source>&amp;View</source>
+ <translation>视图 (&amp;V)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="92"/>
+ <source>Debugging</source>
+ <translation>调试</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="106"/>
+ <source>Tools</source>
+ <translation>工具</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="114"/>
+ <source>&amp;Help</source>
+ <translation>帮助 (&amp;H)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="134"/>
+ <source>Install Files to NAND...</source>
+ <translation>安装文件到 NAND...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="139"/>
+ <source>Load File...</source>
+ <translation>加载文件...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="144"/>
+ <source>Load Folder...</source>
+ <translation>加载文件夹...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="149"/>
+ <source>E&amp;xit</source>
+ <translation>退出 (&amp;X)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="157"/>
+ <source>&amp;Start</source>
+ <translation>开始 (&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="165"/>
+ <source>&amp;Pause</source>
+ <translation>暂停 (&amp;P)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="173"/>
+ <source>&amp;Stop</source>
+ <translation>停止 (&amp;S)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="178"/>
+ <source>Reinitialize keys...</source>
+ <translation>重新生成密钥...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="183"/>
+ <source>About yuzu</source>
+ <translation>关于 yuzu</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="191"/>
+ <source>Single Window Mode</source>
+ <translation>单窗口模式</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="196"/>
+ <source>Configure...</source>
+ <translation>设置...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="204"/>
+ <source>Display Dock Widget Headers</source>
+ <translation>显示停靠小部件的标题</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="212"/>
+ <source>Show Filter Bar</source>
+ <translation>显示搜索栏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="220"/>
+ <source>Show Status Bar</source>
+ <translation>显示状态栏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="225"/>
+ <source>Reset Window Size</source>
+ <translation>重置窗口大小</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="233"/>
+ <source>Fullscreen</source>
+ <translation>全屏</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="241"/>
+ <source>Restart</source>
+ <translation>重新启动</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="249"/>
+ <source>Load Amiibo...</source>
+ <translation>加载 Amiibo...</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="257"/>
+ <source>Report Compatibility</source>
+ <translation>报告兼容性</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="265"/>
+ <source>Open Mods Page</source>
+ <translation>打开 Mod 页面</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="270"/>
+ <source>Open Quickstart Guide</source>
+ <translation>查看快速导航</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="275"/>
+ <source>FAQ</source>
+ <translation>FAQ</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="280"/>
+ <source>Open yuzu Folder</source>
+ <translation>打开 yuzu 文件夹</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="288"/>
+ <source>Capture Screenshot</source>
+ <translation>捕获截图</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/main.ui" line="296"/>
+ <source>Configure Current Game..</source>
+ <translation>配置当前游戏...</translation>
+ </message>
+</context>
+<context>
+ <name>MicroProfileDialog</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/profiler.cpp" line="51"/>
+ <source>MicroProfile</source>
+ <translation>MicroProfile</translation>
+ </message>
+</context>
+<context>
+ <name>QObject</name>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="238"/>
+ <source>Installed SD Titles</source>
+ <translation>SD 卡中安装的项目</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="246"/>
+ <source>Installed NAND Titles</source>
+ <translation>NAND 中安装的项目</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="254"/>
+ <source>System Titles</source>
+ <translation>系统项目</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/game_list_p.h" line="296"/>
+ <source>Add New Game Directory</source>
+ <translation>添加游戏目录</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="23"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="32"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="100"/>
+ <source>Shift</source>
+ <translation>Shift</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="25"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="34"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="102"/>
+ <source>Ctrl</source>
+ <translation>Ctrl</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="27"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="36"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="104"/>
+ <source>Alt</source>
+ <translation>Alt</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="37"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="46"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="131"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="181"/>
+ <source>[not set]</source>
+ <translation>[未设置]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="49"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="58"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="157"/>
+ <source>Hat %1 %2</source>
+ <translation>方向键 %1 %2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="56"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="65"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="164"/>
+ <source>Axis %1%2</source>
+ <translation>轴 %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="62"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="71"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="170"/>
+ <source>Button %1</source>
+ <translation>按键 %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_touch_from_button.cpp" line="68"/>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="76"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="176"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="227"/>
+ <source>[unknown]</source>
+ <translation>[未知]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="22"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="90"/>
+ <source>Click 0</source>
+ <translation>点击 0</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="24"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="92"/>
+ <source>Click 1</source>
+ <translation>点击 1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="26"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="94"/>
+ <source>Click 2</source>
+ <translation>点击 2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="28"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="96"/>
+ <source>Click 3</source>
+ <translation>点击 3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_mouse_advanced.cpp" line="30"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="98"/>
+ <source>Click 4</source>
+ <translation>点击 4</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="143"/>
+ <source>GC Axis %1%2</source>
+ <translation>GC 轴 %1%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="147"/>
+ <source>GC Button %1</source>
+ <translation>GC 按键 %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="190"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="210"/>
+ <source>[unused]</source>
+ <translation>[未使用]</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="196"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="202"/>
+ <source>Axis %1</source>
+ <translation>轴 %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="216"/>
+ <location filename="../../src/yuzu/configuration/configure_input_player.cpp" line="222"/>
+ <source>GC Axis %1</source>
+ <translation>GC 轴 %1</translation>
+ </message>
+</context>
+<context>
+ <name>QtErrorDisplay</name>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="22"/>
+ <source>An error has occured.
+Please try again or contact the developer of the software.
+
+Error Code: %1-%2 (0x%3)</source>
+ <translation>发生了一个错误。
+请再试一次或联系开发者。
+
+错误代码: %1-%2 (0x%3)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="35"/>
+ <source>An error occured on %1 at %2.
+Please try again or contact the developer of the software.
+
+Error Code: %3-%4 (0x%5)</source>
+ <translation>在 %2 处的 %1 上发生了一个错误。
+请再试一次或联系开发者。
+
+错误代码: %3-%4 (0x%5)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/error.cpp" line="49"/>
+ <source>An error has occured.
+Error Code: %1-%2 (0x%3)
+
+%4
+
+%5</source>
+ <translation>发生了一个错误。
+错误代码: %1-%2 (0x%3)
+
+%4
+
+%5</translation>
+ </message>
+</context>
+<context>
+ <name>QtProfileSelectionDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="22"/>
+ <source>%1
+%2</source>
+ <comment>%1 is the profile username, %2 is the formatted UUID (e.g. 00112233-4455-6677-8899-AABBCCDDEEFF))</comment>
+ <translation>%1
+%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="51"/>
+ <source>Select a user:</source>
+ <translation>选择一个用户:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="80"/>
+ <source>Users</source>
+ <translation>用户</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/profile_select.cpp" line="111"/>
+ <source>Profile Selector</source>
+ <translation>选择用户</translation>
+ </message>
+</context>
+<context>
+ <name>QtSoftwareKeyboardDialog</name>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="59"/>
+ <source>Enter text:</source>
+ <translation>输入文本:</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/applets/software_keyboard.cpp" line="101"/>
+ <source>Software Keyboard</source>
+ <translation>软件键盘</translation>
+ </message>
+</context>
+<context>
+ <name>SequenceDialog</name>
+ <message>
+ <location filename="../../src/yuzu/util/sequence_dialog/sequence_dialog.cpp" line="11"/>
+ <source>Enter a hotkey</source>
+ <translation>键入热键</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeCallstack</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="147"/>
+ <source>Call stack</source>
+ <translation>调用栈</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeMutexInfo</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="127"/>
+ <source>waiting for mutex 0x%1</source>
+ <translation>正在等待互斥锁 0x%1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="134"/>
+ <source>has waiters: %1</source>
+ <translation>等待中: %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="136"/>
+ <source>owner handle: 0x%1</source>
+ <translation>所有者句柄: 0x%1</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeObjectList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="223"/>
+ <source>waiting for all objects</source>
+ <translation>正在等待所有对象</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="224"/>
+ <source>waiting for one of the following objects</source>
+ <translation>正在等待下列对象中的一个</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeSynchronizationObject</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="185"/>
+ <source>[%1]%2 %3</source>
+ <translation>[%1]%2 %3</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="208"/>
+ <source>waited by no thread</source>
+ <translation>没有等待的线程</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThread</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="243"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="248"/>
+ <source>running</source>
+ <translation>运行中</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="250"/>
+ <source>ready</source>
+ <translation>就绪</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="253"/>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="257"/>
+ <source>paused</source>
+ <translation>暂停</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="260"/>
+ <source>waiting for HLE return</source>
+ <translation>等待 HLE 返回</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="263"/>
+ <source>sleeping</source>
+ <translation>睡眠</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="266"/>
+ <source>waiting for IPC reply</source>
+ <translation>等待 IPC 响应</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="269"/>
+ <source>waiting for objects</source>
+ <translation>等待对象</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="272"/>
+ <source>waiting for mutex</source>
+ <translation>等待互斥锁</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="275"/>
+ <source>waiting for condition variable</source>
+ <translation>等待条件变量</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="278"/>
+ <source>waiting for address arbiter</source>
+ <translation>等待 address arbiter</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="281"/>
+ <source>dormant</source>
+ <translation>休眠</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="284"/>
+ <source>dead</source>
+ <translation>死亡</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="289"/>
+ <source> PC = 0x%1 LR = 0x%2</source>
+ <translation>PC = 0x%1 LR = 0x%2</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="342"/>
+ <source>ideal</source>
+ <translation>ideal</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="348"/>
+ <source>core %1</source>
+ <translation>核心 %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="351"/>
+ <source>Unknown processor %1</source>
+ <translation>未知的处理器 %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="355"/>
+ <source>processor = %1</source>
+ <translation>处理器 = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="357"/>
+ <source>ideal core = %1</source>
+ <translation>理想核心 = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="359"/>
+ <source>affinity mask = %1</source>
+ <translation>关联掩码 = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="360"/>
+ <source>thread id = %1</source>
+ <translation>线程 ID = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="361"/>
+ <source>priority = %1(current) / %2(normal)</source>
+ <translation>优先级 = %1 (实时) / %2 (正常)</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="365"/>
+ <source>last running ticks = %1</source>
+ <translation>最后运行频率 = %1</translation>
+ </message>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="372"/>
+ <source>not waiting for mutex</source>
+ <translation>未等待互斥锁</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeThreadList</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="394"/>
+ <source>waited by thread</source>
+ <translation>等待中的线程</translation>
+ </message>
+</context>
+<context>
+ <name>WaitTreeWidget</name>
+ <message>
+ <location filename="../../src/yuzu/debugger/wait_tree.cpp" line="466"/>
+ <source>Wait Tree</source>
+ <translation>等待树</translation>
+ </message>
+</context>
+</TS> \ No newline at end of file
diff --git a/dist/license.md b/dist/license.md
index f1ff35c95..e9bc87656 100644
--- a/dist/license.md
+++ b/dist/license.md
@@ -5,6 +5,7 @@ Icon Name | License | Origin/Author
qt_themes/default/icons/16x16/checked.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/failed.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
+qt_themes/default/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/default/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -12,6 +13,7 @@ qt_themes/default/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/default/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/default/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
+qt_themes/qdarkstyle/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/qdarkstyle/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
@@ -19,6 +21,7 @@ qt_themes/qdarkstyle/icons/48x48/folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/qdarkstyle/icons/48x48/plus.png | CC0 1.0 | Designed by BreadFish64 from the Citra team
qt_themes/qdarkstyle/icons/48x48/sd_card.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/16x16/lock.png | CC BY-ND 3.0 | https://icons8.com
+qt_themes/colorful/icons/16x16/view-refresh.png | Apache 2.0 | https://material.io
qt_themes/colorful/icons/256x256/plus_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/bad_folder.png | CC BY-ND 3.0 | https://icons8.com
qt_themes/colorful/icons/48x48/chip.png | CC BY-ND 3.0 | https://icons8.com
diff --git a/dist/qt_themes/colorful_dark/icons/16x16/refresh.png b/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_dark/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png b/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_dark/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_dark/style.qrc b/dist/qt_themes/colorful_dark/style.qrc
index 27a6cc87d..0abcb4e83 100644
--- a/dist/qt_themes/colorful_dark/style.qrc
+++ b/dist/qt_themes/colorful_dark/style.qrc
@@ -2,6 +2,7 @@
<qresource prefix="icons/colorful_dark">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
+ <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_midnight_blue/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/colorful_midnight_blue/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/colorful_midnight_blue/style.qrc b/dist/qt_themes/colorful_midnight_blue/style.qrc
index fd33bc850..bf367099a 100644
--- a/dist/qt_themes/colorful_midnight_blue/style.qrc
+++ b/dist/qt_themes/colorful_midnight_blue/style.qrc
@@ -2,6 +2,7 @@
<qresource prefix="icons/colorful_midnight_blue">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
+ <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">../colorful/icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">../colorful/icons/48x48/chip.png</file>
<file alias="48x48/folder.png">../colorful/icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/default/default.qrc b/dist/qt_themes/default/default.qrc
index c51fdb26c..2182f33f3 100644
--- a/dist/qt_themes/default/default.qrc
+++ b/dist/qt_themes/default/default.qrc
@@ -4,6 +4,7 @@
<file alias="16x16/checked.png">icons/16x16/checked.png</file>
<file alias="16x16/failed.png">icons/16x16/failed.png</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
+ <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
diff --git a/dist/qt_themes/default/icons/16x16/refresh.png b/dist/qt_themes/default/icons/16x16/refresh.png
new file mode 100644
index 000000000..69f9474ac
--- /dev/null
+++ b/dist/qt_themes/default/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/default/icons/16x16/view-refresh.png b/dist/qt_themes/default/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..69f9474ac
--- /dev/null
+++ b/dist/qt_themes/default/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/default/style.qss b/dist/qt_themes/default/style.qss
index 6b5953e38..836dd25ca 100644
--- a/dist/qt_themes/default/style.qss
+++ b/dist/qt_themes/default/style.qss
@@ -1,3 +1,7 @@
+QAbstractSpinBox {
+ min-height: 19px;
+}
+
QPushButton#TogglableStatusBarButton {
color: #959595;
border: 1px solid transparent;
@@ -30,6 +34,250 @@ QPushButton#RendererStatusBarButton:checked {
color: #e85c00;
}
-QPushButton#RendererStatusBarButton:!checked{
+QPushButton#RendererStatusBarButton:!checked {
color: #0066ff;
}
+
+QPushButton#buttonRefreshDevices {
+ min-width: 21px;
+ min-height: 21px;
+ max-width: 21px;
+ max-height: 21px;
+}
+
+QWidget#bottomPerGameInput,
+QWidget#topControllerApplet,
+QWidget#bottomControllerApplet,
+QGroupBox#groupPlayer1Connected:checked,
+QGroupBox#groupPlayer2Connected:checked,
+QGroupBox#groupPlayer3Connected:checked,
+QGroupBox#groupPlayer4Connected:checked,
+QGroupBox#groupPlayer5Connected:checked,
+QGroupBox#groupPlayer6Connected:checked,
+QGroupBox#groupPlayer7Connected:checked,
+QGroupBox#groupPlayer8Connected:checked {
+ background-color: #f5f5f5;
+}
+
+QWidget#topControllerApplet {
+ border-bottom: 1px solid #828790
+}
+
+QWidget#bottomPerGameInput,
+QWidget#bottomControllerApplet {
+ border-top: 1px solid #828790
+}
+
+QWidget#topPerGameInput,
+QWidget#middleControllerApplet {
+ background-color: #fff;
+}
+
+QWidget#topPerGameInput QComboBox,
+QWidget#middleControllerApplet QComboBox {
+ width: 120px;
+}
+
+QWidget#connectedControllers {
+ background: transparent;
+}
+
+QWidget#playersSupported,
+QWidget#controllersSupported,
+QWidget#controllerSupported1,
+QWidget#controllerSupported2,
+QWidget#controllerSupported3,
+QWidget#controllerSupported4,
+QWidget#controllerSupported5,
+QWidget#controllerSupported6 {
+ border: none;
+ background: transparent;
+}
+
+QGroupBox#groupPlayer1Connected,
+QGroupBox#groupPlayer2Connected,
+QGroupBox#groupPlayer3Connected,
+QGroupBox#groupPlayer4Connected,
+QGroupBox#groupPlayer5Connected,
+QGroupBox#groupPlayer6Connected,
+QGroupBox#groupPlayer7Connected,
+QGroupBox#groupPlayer8Connected {
+ border: 1px solid #828790;
+ border-radius: 3px;
+ padding: 0px;
+ min-height: 98px;
+ max-height: 98px;
+}
+
+QGroupBox#groupPlayer1Connected:unchecked,
+QGroupBox#groupPlayer2Connected:unchecked,
+QGroupBox#groupPlayer3Connected:unchecked,
+QGroupBox#groupPlayer4Connected:unchecked,
+QGroupBox#groupPlayer5Connected:unchecked,
+QGroupBox#groupPlayer6Connected:unchecked,
+QGroupBox#groupPlayer7Connected:unchecked,
+QGroupBox#groupPlayer8Connected:unchecked {
+ border: 1px solid #d9d9d9;
+}
+
+QGroupBox#groupPlayer1Connected::title,
+QGroupBox#groupPlayer2Connected::title,
+QGroupBox#groupPlayer3Connected::title,
+QGroupBox#groupPlayer4Connected::title,
+QGroupBox#groupPlayer5Connected::title,
+QGroupBox#groupPlayer6Connected::title,
+QGroupBox#groupPlayer7Connected::title,
+QGroupBox#groupPlayer8Connected::title {
+ subcontrol-origin: margin;
+ subcontrol-position: top left;
+ padding-left: 0px;
+ padding-right: 0px;
+ padding-top: 1px;
+ margin-left: 0px;
+ margin-right: -4px;
+ margin-bottom: 4px;
+}
+
+QCheckBox#checkboxPlayer1Connected,
+QCheckBox#checkboxPlayer2Connected,
+QCheckBox#checkboxPlayer3Connected,
+QCheckBox#checkboxPlayer4Connected,
+QCheckBox#checkboxPlayer5Connected,
+QCheckBox#checkboxPlayer6Connected,
+QCheckBox#checkboxPlayer7Connected,
+QCheckBox#checkboxPlayer8Connected {
+ spacing: 0px;
+}
+
+QWidget#Player1LEDs QCheckBox,
+QWidget#Player2LEDs QCheckBox,
+QWidget#Player3LEDs QCheckBox,
+QWidget#Player4LEDs QCheckBox,
+QWidget#Player5LEDs QCheckBox,
+QWidget#Player6LEDs QCheckBox,
+QWidget#Player7LEDs QCheckBox,
+QWidget#Player8LEDs QCheckBox {
+ spacing: 0px;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator,
+QWidget#Player2LEDs QCheckBox::indicator,
+QWidget#Player3LEDs QCheckBox::indicator,
+QWidget#Player4LEDs QCheckBox::indicator,
+QWidget#Player5LEDs QCheckBox::indicator,
+QWidget#Player6LEDs QCheckBox::indicator,
+QWidget#Player7LEDs QCheckBox::indicator,
+QWidget#Player8LEDs QCheckBox::indicator {
+ width: 6px;
+ height: 6px;
+ margin-left: 0px;
+}
+
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer1Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer2Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer3Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer4Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer5Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer6Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer7Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer8Connected::indicator {
+ width: 12px;
+ height: 12px;
+}
+
+QCheckBox#checkboxPlayer1Connected::indicator,
+QCheckBox#checkboxPlayer2Connected::indicator,
+QCheckBox#checkboxPlayer3Connected::indicator,
+QCheckBox#checkboxPlayer4Connected::indicator,
+QCheckBox#checkboxPlayer5Connected::indicator,
+QCheckBox#checkboxPlayer6Connected::indicator,
+QCheckBox#checkboxPlayer7Connected::indicator,
+QCheckBox#checkboxPlayer8Connected::indicator {
+ width: 14px;
+ height: 14px;
+}
+
+QGroupBox#groupPlayer1Connected::indicator,
+QGroupBox#groupPlayer2Connected::indicator,
+QGroupBox#groupPlayer3Connected::indicator,
+QGroupBox#groupPlayer4Connected::indicator,
+QGroupBox#groupPlayer5Connected::indicator,
+QGroupBox#groupPlayer6Connected::indicator,
+QGroupBox#groupPlayer7Connected::indicator,
+QGroupBox#groupPlayer8Connected::indicator {
+ width: 16px;
+ height: 16px;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator:checked,
+QWidget#Player2LEDs QCheckBox::indicator:checked,
+QWidget#Player3LEDs QCheckBox::indicator:checked,
+QWidget#Player4LEDs QCheckBox::indicator:checked,
+QWidget#Player5LEDs QCheckBox::indicator:checked,
+QWidget#Player6LEDs QCheckBox::indicator:checked,
+QWidget#Player7LEDs QCheckBox::indicator:checked,
+QWidget#Player8LEDs QCheckBox::indicator:checked,
+QGroupBox#groupPlayer1Connected::indicator:checked,
+QGroupBox#groupPlayer2Connected::indicator:checked,
+QGroupBox#groupPlayer3Connected::indicator:checked,
+QGroupBox#groupPlayer4Connected::indicator:checked,
+QGroupBox#groupPlayer5Connected::indicator:checked,
+QGroupBox#groupPlayer6Connected::indicator:checked,
+QGroupBox#groupPlayer7Connected::indicator:checked,
+QGroupBox#groupPlayer8Connected::indicator:checked,
+QCheckBox#checkboxPlayer1Connected::indicator:checked,
+QCheckBox#checkboxPlayer2Connected::indicator:checked,
+QCheckBox#checkboxPlayer3Connected::indicator:checked,
+QCheckBox#checkboxPlayer4Connected::indicator:checked,
+QCheckBox#checkboxPlayer5Connected::indicator:checked,
+QCheckBox#checkboxPlayer6Connected::indicator:checked,
+QCheckBox#checkboxPlayer7Connected::indicator:checked,
+QCheckBox#checkboxPlayer8Connected::indicator:checked,
+QGroupBox#groupConnectedController::indicator:checked {
+ border-radius: 2px;
+ border: 1px solid #929192;
+ background: #39ff14;
+ image: none;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator:unchecked,
+QWidget#Player2LEDs QCheckBox::indicator:unchecked,
+QWidget#Player3LEDs QCheckBox::indicator:unchecked,
+QWidget#Player4LEDs QCheckBox::indicator:unchecked,
+QWidget#Player5LEDs QCheckBox::indicator:unchecked,
+QWidget#Player6LEDs QCheckBox::indicator:unchecked,
+QWidget#Player7LEDs QCheckBox::indicator:unchecked,
+QWidget#Player8LEDs QCheckBox::indicator:unchecked,
+QGroupBox#groupPlayer1Connected::indicator:unchecked,
+QGroupBox#groupPlayer2Connected::indicator:unchecked,
+QGroupBox#groupPlayer3Connected::indicator:unchecked,
+QGroupBox#groupPlayer4Connected::indicator:unchecked,
+QGroupBox#groupPlayer5Connected::indicator:unchecked,
+QGroupBox#groupPlayer6Connected::indicator:unchecked,
+QGroupBox#groupPlayer7Connected::indicator:unchecked,
+QGroupBox#groupPlayer8Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
+QGroupBox#groupConnectedController::indicator:unchecked {
+ border-radius: 2px;
+ border: 1px solid #929192;
+ background: transparent;
+ image: none;
+}
+
+QWidget#controllerPlayer1,
+QWidget#controllerPlayer2,
+QWidget#controllerPlayer3,
+QWidget#controllerPlayer4,
+QWidget#controllerPlayer5,
+QWidget#controllerPlayer6,
+QWidget#controllerPlayer7,
+QWidget#controllerPlayer8 {
+ background: transparent;
+}
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png b/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle/style.qrc b/dist/qt_themes/qdarkstyle/style.qrc
index c2c14c28a..2b91204f3 100644
--- a/dist/qt_themes/qdarkstyle/style.qrc
+++ b/dist/qt_themes/qdarkstyle/style.qrc
@@ -2,6 +2,7 @@
<qresource prefix="icons/qdarkstyle">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
+ <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
@@ -51,6 +52,6 @@
<file>rc/radio_unchecked.png</file>
</qresource>
<qresource prefix="qdarkstyle">
- <file>style.qss</file>
+ <file>style.qss</file>
</qresource>
</RCC>
diff --git a/dist/qt_themes/qdarkstyle/style.qss b/dist/qt_themes/qdarkstyle/style.qss
index 2926a05fa..2a1e8ddeb 100644
--- a/dist/qt_themes/qdarkstyle/style.qss
+++ b/dist/qt_themes/qdarkstyle/style.qss
@@ -40,8 +40,8 @@ QCheckBox:disabled {
QCheckBox::indicator,
QGroupBox::indicator {
- width: 18px;
- height: 18px;
+ width: 16px;
+ height: 16px;
}
QGroupBox::indicator {
@@ -99,12 +99,19 @@ QGroupBox::indicator:unchecked:disabled {
}
QRadioButton {
- spacing: 5px;
- outline: none;
color: #eff0f1;
+ spacing: 3px;
+ padding: 0px;
+ border: none;
+ outline: none;
margin-bottom: 2px;
}
+QGroupBox QRadioButton {
+ padding-left: 0px;
+ padding-right: 7px;
+}
+
QRadioButton:disabled {
color: #76797C;
}
@@ -522,13 +529,12 @@ QToolButton#qt_toolbar_ext_button {
QPushButton {
color: #eff0f1;
- border-width: 1px;
- border-color: #54575B;
- border-style: solid;
- padding: 6px 4px;
+ border: 1px solid #54575B;
border-radius: 2px;
+ padding: 5px 0px 5px 0px;
outline: none;
min-width: 100px;
+ min-height: 13px;
background-color: #232629;
}
@@ -553,8 +559,9 @@ QComboBox {
selection-background-color: #3daee9;
border: 1px solid #54575B;
border-radius: 2px;
- padding: 4px 6px;
- min-width: 75px;
+ padding: 0px 4px 0px 4px;
+ min-width: 60px;
+ min-height: 23px;
background-color: #232629;
}
@@ -608,26 +615,26 @@ QComboBox::down-arrow:focus {
}
QAbstractSpinBox {
- padding: 4px 6px;
border: 1px solid #54575B;
background-color: #232629;
color: #eff0f1;
border-radius: 2px;
- min-width: 75px;
+ min-width: 52px;
+ min-height: 23px;
}
QAbstractSpinBox:up-button {
background-color: transparent;
subcontrol-origin: border;
subcontrol-position: center right;
- left: -6px;
+ left: -2px;
}
QAbstractSpinBox:down-button {
background-color: transparent;
subcontrol-origin: border;
subcontrol-position: center left;
- right: -6px;
+ right: -2px;
}
QAbstractSpinBox::up-arrow,
@@ -1237,6 +1244,7 @@ QPlainTextEdit:disabled {
background-color: #2b2e31;
}
+
QPushButton#TogglableStatusBarButton {
min-width: 0px;
color: #656565;
@@ -1271,6 +1279,288 @@ QPushButton#RendererStatusBarButton:checked {
color: #e85c00;
}
-QPushButton#RendererStatusBarButton:!checked{
- color: #00ccdd;
+QPushButton#RendererStatusBarButton:!checked {
+ color: #00ccdd;
+}
+
+QPushButton#buttonRefreshDevices {
+ min-width: 23px;
+ min-height: 23px;
+ max-width: 23px;
+ max-height: 23px;
+ padding: 0px 0px;
+}
+
+QSpinBox#spinboxLStickRange,
+QSpinBox#spinboxRStickRange,
+QSpinBox#vibrationSpinPlayer1,
+QSpinBox#vibrationSpinPlayer2,
+QSpinBox#vibrationSpinPlayer3,
+QSpinBox#vibrationSpinPlayer4,
+QSpinBox#vibrationSpinPlayer5,
+QSpinBox#vibrationSpinPlayer6,
+QSpinBox#vibrationSpinPlayer7,
+QSpinBox#vibrationSpinPlayer8 {
+ min-width: 68px;
+}
+
+QDialog#ConfigureVibration QGroupBox::indicator,
+QGroupBox#motionGroup::indicator,
+QGroupBox#vibrationGroup::indicator {
+ margin-left: 0px;
+}
+
+QDialog#ConfigureVibration QGroupBox::title,
+QGroupBox#motionGroup::title,
+QGroupBox#vibrationGroup::title {
+ spacing: 2px;
+ padding-left: 1px;
+ padding-right: 1px;
+}
+
+QWidget#bottomPerGameInput,
+QWidget#topControllerApplet,
+QWidget#bottomControllerApplet,
+QGroupBox#groupPlayer1Connected:checked,
+QGroupBox#groupPlayer2Connected:checked,
+QGroupBox#groupPlayer3Connected:checked,
+QGroupBox#groupPlayer4Connected:checked,
+QGroupBox#groupPlayer5Connected:checked,
+QGroupBox#groupPlayer6Connected:checked,
+QGroupBox#groupPlayer7Connected:checked,
+QGroupBox#groupPlayer8Connected:checked {
+ background-color: #232629;
+}
+
+QWidget#topPerGameInput,
+QWidget#middleControllerApplet {
+ background-color: #31363b;
+}
+
+QWidget#topPerGameInput QComboBox,
+QWidget#middleControllerApplet QComboBox {
+ width: 120px;
+}
+
+QWidget#connectedControllers {
+ background: transparent;
+}
+
+QWidget#playersSupported,
+QWidget#controllersSupported,
+QWidget#controllerSupported1,
+QWidget#controllerSupported2,
+QWidget#controllerSupported3,
+QWidget#controllerSupported4,
+QWidget#controllerSupported5,
+QWidget#controllerSupported6 {
+ border: none;
+ background: transparent;
+}
+
+QGroupBox#groupPlayer1Connected,
+QGroupBox#groupPlayer2Connected,
+QGroupBox#groupPlayer3Connected,
+QGroupBox#groupPlayer4Connected,
+QGroupBox#groupPlayer5Connected,
+QGroupBox#groupPlayer6Connected,
+QGroupBox#groupPlayer7Connected,
+QGroupBox#groupPlayer8Connected {
+ border: 1px solid #76797c;
+ border-radius: 3px;
+ padding: 0px;
+ min-height: 98px;
+ max-height: 98px;
+ margin-top: 0px;
+}
+
+QGroupBox#groupPlayer1Connected:unchecked,
+QGroupBox#groupPlayer2Connected:unchecked,
+QGroupBox#groupPlayer3Connected:unchecked,
+QGroupBox#groupPlayer4Connected:unchecked,
+QGroupBox#groupPlayer5Connected:unchecked,
+QGroupBox#groupPlayer6Connected:unchecked,
+QGroupBox#groupPlayer7Connected:unchecked,
+QGroupBox#groupPlayer8Connected:unchecked {
+ border: 1px solid #54575b;
+}
+
+QGroupBox#groupPlayer1Connected::title,
+QGroupBox#groupPlayer2Connected::title,
+QGroupBox#groupPlayer3Connected::title,
+QGroupBox#groupPlayer4Connected::title,
+QGroupBox#groupPlayer5Connected::title,
+QGroupBox#groupPlayer6Connected::title,
+QGroupBox#groupPlayer7Connected::title,
+QGroupBox#groupPlayer8Connected::title {
+ subcontrol-origin: margin;
+ subcontrol-position: top left;
+ padding-left: 0px;
+ padding-right: 0px;
+ padding-top: 1px;
+ margin-left: -2px;
+ margin-right: -4px;
+ margin-bottom: 6px;
+}
+
+QCheckBox#checkboxPlayer1Connected,
+QCheckBox#checkboxPlayer2Connected,
+QCheckBox#checkboxPlayer3Connected,
+QCheckBox#checkboxPlayer4Connected,
+QCheckBox#checkboxPlayer5Connected,
+QCheckBox#checkboxPlayer6Connected,
+QCheckBox#checkboxPlayer7Connected,
+QCheckBox#checkboxPlayer8Connected {
+ spacing: 0px;
+}
+
+QWidget#Player1LEDs,
+QWidget#Player2LEDs,
+QWidget#Player3LEDs,
+QWidget#Player4LEDs,
+QWidget#Player5LEDs,
+QWidget#Player6LEDs,
+QWidget#Player7LEDs,
+QWidget#Player8LEDs {
+ background: transparent;
+}
+
+QWidget#Player1LEDs QCheckBox,
+QWidget#Player2LEDs QCheckBox,
+QWidget#Player3LEDs QCheckBox,
+QWidget#Player4LEDs QCheckBox,
+QWidget#Player5LEDs QCheckBox,
+QWidget#Player6LEDs QCheckBox,
+QWidget#Player7LEDs QCheckBox,
+QWidget#Player8LEDs QCheckBox {
+ spacing: 0px;
+ margin-bottom: 0px;
+ margin-right: 0px;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator,
+QWidget#Player2LEDs QCheckBox::indicator,
+QWidget#Player3LEDs QCheckBox::indicator,
+QWidget#Player4LEDs QCheckBox::indicator,
+QWidget#Player5LEDs QCheckBox::indicator,
+QWidget#Player6LEDs QCheckBox::indicator,
+QWidget#Player7LEDs QCheckBox::indicator,
+QWidget#Player8LEDs QCheckBox::indicator {
+ width: 6px;
+ height: 6px;
+ margin-left: 0px;
+}
+
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer1Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer2Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer3Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer4Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer5Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer6Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer7Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer8Connected::indicator {
+ width: 12px;
+ height: 12px;
+}
+
+QCheckBox#checkboxPlayer1Connected::indicator,
+QCheckBox#checkboxPlayer2Connected::indicator,
+QCheckBox#checkboxPlayer3Connected::indicator,
+QCheckBox#checkboxPlayer4Connected::indicator,
+QCheckBox#checkboxPlayer5Connected::indicator,
+QCheckBox#checkboxPlayer6Connected::indicator,
+QCheckBox#checkboxPlayer7Connected::indicator,
+QCheckBox#checkboxPlayer8Connected::indicator {
+ width: 14px;
+ height: 14px;
+}
+
+QGroupBox#groupPlayer1Connected::indicator,
+QGroupBox#groupPlayer2Connected::indicator,
+QGroupBox#groupPlayer3Connected::indicator,
+QGroupBox#groupPlayer4Connected::indicator,
+QGroupBox#groupPlayer5Connected::indicator,
+QGroupBox#groupPlayer6Connected::indicator,
+QGroupBox#groupPlayer7Connected::indicator,
+QGroupBox#groupPlayer8Connected::indicator {
+ width: 16px;
+ height: 16px;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator:checked,
+QWidget#Player2LEDs QCheckBox::indicator:checked,
+QWidget#Player3LEDs QCheckBox::indicator:checked,
+QWidget#Player4LEDs QCheckBox::indicator:checked,
+QWidget#Player5LEDs QCheckBox::indicator:checked,
+QWidget#Player6LEDs QCheckBox::indicator:checked,
+QWidget#Player7LEDs QCheckBox::indicator:checked,
+QWidget#Player8LEDs QCheckBox::indicator:checked,
+QGroupBox#groupPlayer1Connected::indicator:checked,
+QGroupBox#groupPlayer2Connected::indicator:checked,
+QGroupBox#groupPlayer3Connected::indicator:checked,
+QGroupBox#groupPlayer4Connected::indicator:checked,
+QGroupBox#groupPlayer5Connected::indicator:checked,
+QGroupBox#groupPlayer6Connected::indicator:checked,
+QGroupBox#groupPlayer7Connected::indicator:checked,
+QGroupBox#groupPlayer8Connected::indicator:checked,
+QCheckBox#checkboxPlayer1Connected::indicator:checked,
+QCheckBox#checkboxPlayer2Connected::indicator:checked,
+QCheckBox#checkboxPlayer3Connected::indicator:checked,
+QCheckBox#checkboxPlayer4Connected::indicator:checked,
+QCheckBox#checkboxPlayer5Connected::indicator:checked,
+QCheckBox#checkboxPlayer6Connected::indicator:checked,
+QCheckBox#checkboxPlayer7Connected::indicator:checked,
+QCheckBox#checkboxPlayer8Connected::indicator:checked,
+QGroupBox#groupConnectedController::indicator:checked {
+ border-radius: 2px;
+ border: 1px solid #929192;
+ background: #39ff14;
+ image: none;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator:unchecked,
+QWidget#Player2LEDs QCheckBox::indicator:unchecked,
+QWidget#Player3LEDs QCheckBox::indicator:unchecked,
+QWidget#Player4LEDs QCheckBox::indicator:unchecked,
+QWidget#Player5LEDs QCheckBox::indicator:unchecked,
+QWidget#Player6LEDs QCheckBox::indicator:unchecked,
+QWidget#Player7LEDs QCheckBox::indicator:unchecked,
+QWidget#Player8LEDs QCheckBox::indicator:unchecked,
+QGroupBox#groupPlayer1Connected::indicator:unchecked,
+QGroupBox#groupPlayer2Connected::indicator:unchecked,
+QGroupBox#groupPlayer3Connected::indicator:unchecked,
+QGroupBox#groupPlayer4Connected::indicator:unchecked,
+QGroupBox#groupPlayer5Connected::indicator:unchecked,
+QGroupBox#groupPlayer6Connected::indicator:unchecked,
+QGroupBox#groupPlayer7Connected::indicator:unchecked,
+QGroupBox#groupPlayer8Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
+QGroupBox#groupConnectedController::indicator:unchecked {
+ border-radius: 2px;
+ border: 1px solid #929192;
+ background: transparent;
+ image: none;
+}
+
+QWidget#controllerPlayer1,
+QWidget#controllerPlayer2,
+QWidget#controllerPlayer3,
+QWidget#controllerPlayer4,
+QWidget#controllerPlayer5,
+QWidget#controllerPlayer6,
+QWidget#controllerPlayer7,
+QWidget#controllerPlayer8 {
+ background: transparent;
+}
+
+/* touchscreen mapping widget */
+TouchScreenPreview {
+ qproperty-dotHighlightColor: #3daee9;
}
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
new file mode 100644
index 000000000..d4afd76f9
--- /dev/null
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/icons/16x16/view-refresh.png
Binary files differ
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
index 1b7686f15..579e73ece 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qrc
@@ -2,6 +2,7 @@
<qresource prefix="icons/qdarkstyle_midnight_blue">
<file alias="index.theme">icons/index.theme</file>
<file alias="16x16/lock.png">icons/16x16/lock.png</file>
+ <file alias="16x16/view-refresh.png">icons/16x16/view-refresh.png</file>
<file alias="48x48/bad_folder.png">icons/48x48/bad_folder.png</file>
<file alias="48x48/chip.png">icons/48x48/chip.png</file>
<file alias="48x48/folder.png">icons/48x48/folder.png</file>
@@ -220,6 +221,6 @@
<file>rc/window_undock_pressed@2x.png</file>
</qresource>
<qresource prefix="qdarkstyle_midnight_blue">
- <file>style.qss</file>
+ <file>style.qss</file>
</qresource>
</RCC>
diff --git a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
index 9c24b0d07..70e540b06 100644
--- a/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
+++ b/dist/qt_themes/qdarkstyle_midnight_blue/style.qss
@@ -138,8 +138,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qstatusbar
--------------------------------------------------------------------------- */
QStatusBar {
- border: 1px solid #32414B;
- /* Fixes Spyder #9120, #9121 */
background: #32414B;
/* Fixes #205, white vertical borders separating items */
}
@@ -161,6 +159,7 @@ QStatusBar QToolTip {
QStatusBar QLabel {
/* Fixes Spyder #9120, #9121 */
background: transparent;
+ padding: 0px;
}
/* QCheckBox --------------------------------------------------------------
@@ -173,8 +172,8 @@ QCheckBox {
color: #F0F0F0;
spacing: 4px;
outline: none;
- padding-top: 4px;
- padding-bottom: 4px;
+ padding-top: 2px;
+ padding-bottom: 2px;
}
QCheckBox:focus {
@@ -239,18 +238,16 @@ QGroupBox {
font-weight: bold;
border: 1px solid #32414B;
border-radius: 4px;
- padding: 4px;
- margin-top: 16px;
+ margin-top: 12px;
+ padding: 2px;
}
QGroupBox::title {
subcontrol-origin: margin;
subcontrol-position: top left;
- left: 3px;
padding-left: 3px;
padding-right: 5px;
- padding-top: 8px;
- padding-bottom: 16px;
+ padding-top: 2px;
}
QGroupBox::indicator {
@@ -301,6 +298,11 @@ QRadioButton {
outline: none;
}
+QGroupBox QRadioButton {
+ padding-left: 0px;
+ padding-right: 7px;
+}
+
QRadioButton:focus {
border: none;
}
@@ -324,7 +326,6 @@ QRadioButton QWidget {
QRadioButton::indicator {
border: none;
outline: none;
- margin-left: 4px;
height: 16px;
width: 16px;
}
@@ -367,28 +368,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenubar
--------------------------------------------------------------------------- */
QMenuBar {
background-color: #32414B;
- padding: 2px;
- border: 1px solid #19232D;
color: #F0F0F0;
}
-QMenuBar:focus {
- border: 1px solid #148CD2;
-}
-
QMenuBar::item {
background: transparent;
- padding: 4px;
}
QMenuBar::item:selected {
- padding: 4px;
background: transparent;
border: 0px solid #32414B;
}
QMenuBar::item:pressed {
- padding: 4px;
border: 0px solid #32414B;
background-color: #148CD2;
color: #F0F0F0;
@@ -396,6 +388,7 @@ QMenuBar::item:pressed {
padding-bottom: 0px;
}
+
/* QMenu ------------------------------------------------------------------
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qmenu
@@ -482,7 +475,7 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
--------------------------------------------------------------------------- */
QAbstractItemView {
- alternate-background-color: #19232D;
+ alternate-background-color: #1f2933;
color: #F0F0F0;
border: 1px solid #32414B;
border-radius: 4px;
@@ -501,13 +494,13 @@ QAbstractScrollArea {
background-color: #19232D;
border: 1px solid #32414B;
border-radius: 4px;
- padding: 2px;
/* fix #159 */
min-height: 1.25em;
/* fix #159 */
color: #F0F0F0;
}
+
QAbstractScrollArea:disabled {
color: #787878;
}
@@ -796,31 +789,27 @@ QAbstractSpinBox {
background-color: #19232D;
border: 1px solid #32414B;
color: #F0F0F0;
- /* This fixes 103, 111 */
- padding-top: 2px;
- /* This fixes 103, 111 */
- padding-bottom: 2px;
- padding-left: 4px;
- padding-right: 4px;
border-radius: 4px;
- /* min-width: 5px; removed to fix 109 */
+ min-height: 19px;
}
QAbstractSpinBox:up-button {
- background-color: transparent #19232D;
+ background-color: #505F69;
subcontrol-origin: border;
subcontrol-position: top right;
border-left: 1px solid #32414B;
- border-bottom: 1px solid #32414B;
+ border-top: 1px solid #32414B;
+ border-right: 1px solid #32414B;
+ border-top-right-radius: 4px;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
- margin: 1px;
+ margin: 0px;
width: 12px;
- margin-bottom: -1px;
+ margin-bottom: 0px;
}
QAbstractSpinBox::up-arrow, QAbstractSpinBox::up-arrow:disabled, QAbstractSpinBox::up-arrow:off {
- image: url(":/qss_icons/rc/arrow_up_disabled.png");
+ image: url(":/qss_icons/rc/up_arrow.png");
height: 8px;
width: 8px;
}
@@ -830,20 +819,23 @@ QAbstractSpinBox::up-arrow:hover {
}
QAbstractSpinBox:down-button {
- background-color: transparent #19232D;
+ background-color: #505F69;
subcontrol-origin: border;
subcontrol-position: bottom right;
border-left: 1px solid #32414B;
+ border-right: 1px solid #32414B;
+ border-bottom: 1px solid #32414B;
border-top: 1px solid #32414B;
border-top-left-radius: 0;
border-bottom-left-radius: 0;
- margin: 1px;
+ border-bottom-right-radius: 4px;
+ margin: 0px;
width: 12px;
- margin-top: -1px;
+ margin-top: 0px;
}
QAbstractSpinBox::down-arrow, QAbstractSpinBox::down-arrow:disabled, QAbstractSpinBox::down-arrow:off {
- image: url(":/qss_icons/rc/arrow_down_disabled.png");
+ image: url(":/qss_icons/rc/down_arrow.png");
height: 8px;
width: 8px;
}
@@ -1003,10 +995,11 @@ QPushButton {
border: 1px solid #32414B;
color: #F0F0F0;
border-radius: 4px;
- padding: 3px;
+ padding: 3px 0px 3px 0px;
outline: none;
/* Issue #194 - Special case of QPushButton inside dialogs, for better UI */
min-width: 80px;
+ min-height: 13px;
}
QPushButton:disabled {
@@ -1014,14 +1007,14 @@ QPushButton:disabled {
border: 1px solid #32414B;
color: #787878;
border-radius: 4px;
- padding: 3px;
+ padding: 3px 0px 3px 0px;
}
QPushButton:checked {
background-color: #32414B;
border: 1px solid #32414B;
border-radius: 4px;
- padding: 3px;
+ padding: 3px 0px 3px 0px;
outline: none;
}
@@ -1030,7 +1023,7 @@ QPushButton:checked:disabled {
border: 1px solid #32414B;
color: #787878;
border-radius: 4px;
- padding: 3px;
+ padding: 3px 0px 3px 0px;
outline: none;
}
@@ -1199,24 +1192,19 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qcombobox
--------------------------------------------------------------------------- */
QComboBox {
+ background-color: #0f1922;
border: 1px solid #32414B;
border-radius: 4px;
selection-background-color: #1464A0;
- padding-left: 4px;
- padding-right: 36px;
- /* 4 + 16*2 See scrollbar size */
- /* Fixes #103, #111 */
- min-height: 1.5em;
- /* padding-top: 2px; removed to fix #132 */
- /* padding-bottom: 2px; removed to fix #132 */
- /* min-width: 75px; removed to fix #109 */
- /* Needed to remove indicator - fix #132 */
+ padding: 0px 4px 0px 4px;
+ min-width: 60px;
+ min-height: 19px;
}
QComboBox QAbstractItemView {
border: 1px solid #32414B;
border-radius: 0;
- background-color: #19232D;
+ background-color: #0f1922;
selection-background-color: #1464A0;
}
@@ -1285,7 +1273,12 @@ QComboBox::drop-down {
}
QComboBox::down-arrow {
- image: url(":/qss_icons/rc/arrow_down_disabled.png");
+ image: url(":/qss_icons/rc/down_arrow.png");
+ background-color: #505F69;
+ padding: 6px 2px;
+ border: 1px solid #32414B;
+ border-top-right-radius: 4px;
+ border-bottom-right-radius: 4px;
height: 8px;
width: 8px;
}
@@ -1559,12 +1552,12 @@ QTabBar::tab:right:!selected {
QTabBar::tab:top {
background-color: #32414B;
color: #F0F0F0;
+ min-width: 36px;
margin-left: 2px;
- padding-left: 4px;
- padding-right: 4px;
+ padding-left: 8px;
+ padding-right: 8px;
padding-top: 2px;
padding-bottom: 2px;
- min-width: 5px;
border-bottom: 3px solid #32414B;
border-top-left-radius: 3px;
border-top-right-radius: 3px;
@@ -1588,16 +1581,16 @@ QTabBar::tab:top:!selected:hover {
QTabBar::tab:bottom {
color: #F0F0F0;
+ min-width: 36px;
border-top: 3px solid #32414B;
background-color: #32414B;
margin-left: 2px;
- padding-left: 4px;
- padding-right: 4px;
+ padding-left: 8px;
+ padding-right: 8px;
padding-top: 2px;
padding-bottom: 2px;
border-bottom-left-radius: 3px;
border-bottom-right-radius: 3px;
- min-width: 5px;
}
QTabBar::tab:bottom:selected {
@@ -1752,21 +1745,6 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qlistview
https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qtableview
--------------------------------------------------------------------------- */
-QTreeView:branch:selected, QTreeView:branch:hover {
- background: url(":/qss_icons/rc/transparent.png");
-}
-
-QTreeView:branch:has-siblings:!adjoins-item {
- border-image: url(":/qss_icons/rc/branch_line.png") 0;
-}
-
-QTreeView:branch:has-siblings:adjoins-item {
- border-image: url(":/qss_icons/rc/branch_more.png") 0;
-}
-
-QTreeView:branch:!has-children:!has-siblings:adjoins-item {
- border-image: url(":/qss_icons/rc/branch_end.png") 0;
-}
QTreeView:branch:has-children:!has-siblings:closed, QTreeView:branch:closed:has-children:has-siblings {
border-image: none;
@@ -1900,21 +1878,21 @@ https://doc.qt.io/qt-5/stylesheet-examples.html#customizing-qheaderview
--------------------------------------------------------------------------- */
QHeaderView {
- background-color: #32414B;
- border: 0px transparent #32414B;
+ background-color: #19232D;
+ border: 0px transparent #19232D;
padding: 0px;
margin: 0px;
border-radius: 0px;
}
QHeaderView:disabled {
- background-color: #32414B;
- border: 1px transparent #32414B;
+ background-color: #19232D;
+ border: 1px transparent #19232D;
padding: 2px;
}
QHeaderView::section {
- background-color: #32414B;
+ background-color: #19232D;
color: #F0F0F0;
padding: 2px;
border-radius: 0px;
@@ -1934,11 +1912,11 @@ QHeaderView::section:checked:disabled {
QHeaderView::section::horizontal {
padding-left: 4px;
padding-right: 4px;
- border-left: 1px solid #19232D;
+ border-left: 1px solid #32414B;
}
QHeaderView::section::horizontal::first, QHeaderView::section::horizontal::only-one {
- border-left: 1px solid #32414B;
+ border-left: 1px solid #19232D;
}
QHeaderView::section::horizontal:disabled {
@@ -1948,7 +1926,7 @@ QHeaderView::section::horizontal:disabled {
QHeaderView::section::vertical {
padding-left: 4px;
padding-right: 4px;
- border-top: 1px solid #19232D;
+ border-top: 1px solid #32414B;
}
QHeaderView::section::vertical::first, QHeaderView::section::vertical::only-one {
@@ -1962,7 +1940,7 @@ QHeaderView::section::vertical:disabled {
QHeaderView::down-arrow {
/* Those settings (border/width/height/background-color) solve bug */
/* transparent arrow background and size */
- background-color: #32414B;
+ background-color: #19232D;
border: none;
height: 12px;
width: 12px;
@@ -1972,7 +1950,7 @@ QHeaderView::down-arrow {
}
QHeaderView::up-arrow {
- background-color: #32414B;
+ background-color: #19232D;
border: none;
height: 12px;
width: 12px;
@@ -2172,3 +2150,340 @@ PlotWidget {
/* Fix cut labels in plots #134 */
padding: 0px;
}
+
+
+QPushButton#TogglableStatusBarButton {
+ min-width: 0px;
+ color: #656565;
+ border: 1px solid transparent;
+ background-color: transparent;
+ padding: 0px 3px 0px 3px;
+ text-align: center;
+}
+
+QPushButton#TogglableStatusBarButton:checked {
+ color: #ffffff;
+}
+
+QPushButton#TogglableStatusBarButton:hover {
+ border: 1px solid #76797C;
+}
+
+QPushButton#RendererStatusBarButton {
+ min-width: 0px;
+ color: #656565;
+ border: 1px solid transparent;
+ background-color: transparent;
+ padding: 0px 3px 0px 3px;
+ text-align: center;
+}
+
+QPushButton#RendererStatusBarButton:hover {
+ border: 1px solid #76797C;
+}
+
+QPushButton#RendererStatusBarButton:checked {
+ color: #e85c00;
+}
+
+QPushButton#RendererStatusBarButton:!checked {
+ color: #00ccdd;
+}
+
+QPushButton#buttonRefreshDevices {
+ min-width: 19px;
+ min-height: 19px;
+ max-width: 19px;
+ max-height: 19px;
+ padding: 0px 0px;
+}
+
+QSpinBox#spinboxLStickRange,
+QSpinBox#spinboxRStickRange,
+QSpinBox#vibrationSpinPlayer1,
+QSpinBox#vibrationSpinPlayer2,
+QSpinBox#vibrationSpinPlayer3,
+QSpinBox#vibrationSpinPlayer4,
+QSpinBox#vibrationSpinPlayer5,
+QSpinBox#vibrationSpinPlayer6,
+QSpinBox#vibrationSpinPlayer7,
+QSpinBox#vibrationSpinPlayer8 {
+ min-width: 68px;
+}
+
+QDialog#ConfigureVibration QGroupBox::indicator,
+QGroupBox#motionGroup::indicator,
+QGroupBox#vibrationGroup::indicator {
+ margin-left: 0px;
+}
+
+QDialog#ConfigureVibration QGroupBox,
+QWidget#bottomPerGameInput QGroupBox#motionGroup,
+QWidget#bottomPerGameInput QGroupBox#vibrationGroup,
+QWidget#bottomPerGameInput QGroupBox#inputConfigGroup {
+ padding: 0px;
+}
+
+QDialog#ConfigureVibration QGroupBox::title,
+QGroupBox#motionGroup::title,
+QGroupBox#vibrationGroup::title {
+ spacing: 2px;
+ padding-left: 1px;
+ padding-right: 1px;
+}
+
+QListWidget#selectorList {
+ background-color: #0f1922;
+}
+
+QSpinBox,
+QLineEdit,
+QTreeView#hotkey_list,
+QScrollArea#scrollArea QTreeView {
+ background-color: #0f1922;
+}
+
+QWidget#bottomPerGameInput,
+QWidget#topControllerApplet,
+QWidget#bottomControllerApplet,
+QGroupBox#groupPlayer1Connected:checked,
+QGroupBox#groupPlayer2Connected:checked,
+QGroupBox#groupPlayer3Connected:checked,
+QGroupBox#groupPlayer4Connected:checked,
+QGroupBox#groupPlayer5Connected:checked,
+QGroupBox#groupPlayer6Connected:checked,
+QGroupBox#groupPlayer7Connected:checked,
+QGroupBox#groupPlayer8Connected:checked {
+ background-color: #0f1922;
+}
+
+QWidget#topPerGameInput,
+QWidget#middleControllerApplet {
+ background-color: #19232d;
+}
+
+QWidget#topPerGameInput QComboBox,
+QWidget#middleControllerApplet QComboBox {
+ width: 120px;
+}
+
+QWidget#connectedControllers {
+ background: transparent;
+}
+
+QWidget#playersSupported,
+QWidget#controllersSupported,
+QWidget#controllerSupported1,
+QWidget#controllerSupported2,
+QWidget#controllerSupported3,
+QWidget#controllerSupported4,
+QWidget#controllerSupported5,
+QWidget#controllerSupported6 {
+ border: none;
+ background: transparent;
+}
+
+QGroupBox#groupPlayer1Connected,
+QGroupBox#groupPlayer2Connected,
+QGroupBox#groupPlayer3Connected,
+QGroupBox#groupPlayer4Connected,
+QGroupBox#groupPlayer5Connected,
+QGroupBox#groupPlayer6Connected,
+QGroupBox#groupPlayer7Connected,
+QGroupBox#groupPlayer8Connected {
+ border: 1px solid #76797c;
+ border-radius: 3px;
+ padding: 0px;
+ min-height: 98px;
+ max-height: 98px;
+ margin-top: 0px;
+}
+
+
+QGroupBox#groupPlayer1Connected:unchecked,
+QGroupBox#groupPlayer2Connected:unchecked,
+QGroupBox#groupPlayer3Connected:unchecked,
+QGroupBox#groupPlayer4Connected:unchecked,
+QGroupBox#groupPlayer5Connected:unchecked,
+QGroupBox#groupPlayer6Connected:unchecked,
+QGroupBox#groupPlayer7Connected:unchecked,
+QGroupBox#groupPlayer8Connected:unchecked {
+ border: 1px solid #32414b;
+}
+
+QGroupBox#groupPlayer1Connected::title,
+QGroupBox#groupPlayer2Connected::title,
+QGroupBox#groupPlayer3Connected::title,
+QGroupBox#groupPlayer4Connected::title,
+QGroupBox#groupPlayer5Connected::title,
+QGroupBox#groupPlayer6Connected::title,
+QGroupBox#groupPlayer7Connected::title,
+QGroupBox#groupPlayer8Connected::title {
+ subcontrol-origin: margin;
+ subcontrol-position: top left;
+ padding-left: 0px;
+ padding-right: 0px;
+ padding-top: 1px;
+ margin-left: -2px;
+ margin-right: -4px;
+ margin-bottom: 6px;
+}
+
+QCheckBox#checkboxPlayer1Connected,
+QCheckBox#checkboxPlayer2Connected,
+QCheckBox#checkboxPlayer3Connected,
+QCheckBox#checkboxPlayer4Connected,
+QCheckBox#checkboxPlayer5Connected,
+QCheckBox#checkboxPlayer6Connected,
+QCheckBox#checkboxPlayer7Connected,
+QCheckBox#checkboxPlayer8Connected {
+ spacing: 0px;
+}
+
+QWidget#connectedControllers QLabel {
+ padding: 0px;
+}
+
+QWidget#Player1LEDs,
+QWidget#Player2LEDs,
+QWidget#Player3LEDs,
+QWidget#Player4LEDs,
+QWidget#Player5LEDs,
+QWidget#Player6LEDs,
+QWidget#Player7LEDs,
+QWidget#Player8LEDs {
+ background: transparent;
+}
+
+QWidget#Player1LEDs QCheckBox,
+QWidget#Player2LEDs QCheckBox,
+QWidget#Player3LEDs QCheckBox,
+QWidget#Player4LEDs QCheckBox,
+QWidget#Player5LEDs QCheckBox,
+QWidget#Player6LEDs QCheckBox,
+QWidget#Player7LEDs QCheckBox,
+QWidget#Player8LEDs QCheckBox,
+QCheckBox#checkboxPlayer1Connected,
+QCheckBox#checkboxPlayer2Connected,
+QCheckBox#checkboxPlayer3Connected,
+QCheckBox#checkboxPlayer4Connected,
+QCheckBox#checkboxPlayer5Connected,
+QCheckBox#checkboxPlayer6Connected,
+QCheckBox#checkboxPlayer7Connected,
+QCheckBox#checkboxPlayer8Connected {
+ spacing: 0px;
+ padding-top: 0px;
+ padding-bottom: 0px;
+ background: transparent;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator,
+QWidget#Player2LEDs QCheckBox::indicator,
+QWidget#Player3LEDs QCheckBox::indicator,
+QWidget#Player4LEDs QCheckBox::indicator,
+QWidget#Player5LEDs QCheckBox::indicator,
+QWidget#Player6LEDs QCheckBox::indicator,
+QWidget#Player7LEDs QCheckBox::indicator,
+QWidget#Player8LEDs QCheckBox::indicator {
+ width: 6px;
+ height: 6px;
+ margin-left: 0px;
+}
+
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer1Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer2Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer3Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer4Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer5Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer6Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer7Connected::indicator,
+QWidget#bottomPerGameInput QCheckBox#checkboxPlayer8Connected::indicator {
+ width: 12px;
+ height: 12px;
+}
+
+QCheckBox#checkboxPlayer1Connected::indicator,
+QCheckBox#checkboxPlayer2Connected::indicator,
+QCheckBox#checkboxPlayer3Connected::indicator,
+QCheckBox#checkboxPlayer4Connected::indicator,
+QCheckBox#checkboxPlayer5Connected::indicator,
+QCheckBox#checkboxPlayer6Connected::indicator,
+QCheckBox#checkboxPlayer7Connected::indicator,
+QCheckBox#checkboxPlayer8Connected::indicator {
+ width: 14px;
+ height: 14px;
+ margin-left: 0px;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator:checked,
+QWidget#Player2LEDs QCheckBox::indicator:checked,
+QWidget#Player3LEDs QCheckBox::indicator:checked,
+QWidget#Player4LEDs QCheckBox::indicator:checked,
+QWidget#Player5LEDs QCheckBox::indicator:checked,
+QWidget#Player6LEDs QCheckBox::indicator:checked,
+QWidget#Player7LEDs QCheckBox::indicator:checked,
+QWidget#Player8LEDs QCheckBox::indicator:checked,
+QGroupBox#groupPlayer1Connected::indicator:checked,
+QGroupBox#groupPlayer2Connected::indicator:checked,
+QGroupBox#groupPlayer3Connected::indicator:checked,
+QGroupBox#groupPlayer4Connected::indicator:checked,
+QGroupBox#groupPlayer5Connected::indicator:checked,
+QGroupBox#groupPlayer6Connected::indicator:checked,
+QGroupBox#groupPlayer7Connected::indicator:checked,
+QGroupBox#groupPlayer8Connected::indicator:checked,
+QCheckBox#checkboxPlayer1Connected::indicator:checked,
+QCheckBox#checkboxPlayer2Connected::indicator:checked,
+QCheckBox#checkboxPlayer3Connected::indicator:checked,
+QCheckBox#checkboxPlayer4Connected::indicator:checked,
+QCheckBox#checkboxPlayer5Connected::indicator:checked,
+QCheckBox#checkboxPlayer6Connected::indicator:checked,
+QCheckBox#checkboxPlayer7Connected::indicator:checked,
+QCheckBox#checkboxPlayer8Connected::indicator:checked,
+QGroupBox#groupConnectedController::indicator:checked {
+ border-radius: 2px;
+ border: 1px solid #929192;
+ background: #39ff14;
+ image: none;
+}
+
+QWidget#Player1LEDs QCheckBox::indicator:unchecked,
+QWidget#Player2LEDs QCheckBox::indicator:unchecked,
+QWidget#Player3LEDs QCheckBox::indicator:unchecked,
+QWidget#Player4LEDs QCheckBox::indicator:unchecked,
+QWidget#Player5LEDs QCheckBox::indicator:unchecked,
+QWidget#Player6LEDs QCheckBox::indicator:unchecked,
+QWidget#Player7LEDs QCheckBox::indicator:unchecked,
+QWidget#Player8LEDs QCheckBox::indicator:unchecked,
+QGroupBox#groupPlayer1Connected::indicator:unchecked,
+QGroupBox#groupPlayer2Connected::indicator:unchecked,
+QGroupBox#groupPlayer3Connected::indicator:unchecked,
+QGroupBox#groupPlayer4Connected::indicator:unchecked,
+QGroupBox#groupPlayer5Connected::indicator:unchecked,
+QGroupBox#groupPlayer6Connected::indicator:unchecked,
+QGroupBox#groupPlayer7Connected::indicator:unchecked,
+QGroupBox#groupPlayer8Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer1Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer2Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer3Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer4Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer5Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer6Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer7Connected::indicator:unchecked,
+QCheckBox#checkboxPlayer8Connected::indicator:unchecked,
+QGroupBox#groupConnectedController::indicator:unchecked {
+ border-radius: 2px;
+ border: 1px solid #929192;
+ background: #19232d;
+ image: none;
+}
+
+QWidget#controllerPlayer1,
+QWidget#controllerPlayer2,
+QWidget#controllerPlayer3,
+QWidget#controllerPlayer4,
+QWidget#controllerPlayer5,
+QWidget#controllerPlayer6,
+QWidget#controllerPlayer7,
+QWidget#controllerPlayer8 {
+ background: transparent;
+}
diff --git a/externals/CMakeLists.txt b/externals/CMakeLists.txt
index d1dcc403b..421b35890 100644
--- a/externals/CMakeLists.txt
+++ b/externals/CMakeLists.txt
@@ -73,23 +73,29 @@ if (NOT LIBZIP_FOUND)
endif()
if (ENABLE_WEB_SERVICE)
- # LibreSSL
- set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
- add_subdirectory(libressl EXCLUDE_FROM_ALL)
- target_include_directories(ssl INTERFACE ./libressl/include)
- target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
- get_directory_property(OPENSSL_LIBRARIES
- DIRECTORY libressl
- DEFINITION OPENSSL_LIBS)
-
- # lurlparser
- add_subdirectory(lurlparser EXCLUDE_FROM_ALL)
+ find_package(OpenSSL 1.1)
+ if (OPENSSL_FOUND)
+ set(OPENSSL_LIBRARIES OpenSSL::SSL OpenSSL::Crypto)
+ else()
+ # LibreSSL
+ set(LIBRESSL_SKIP_INSTALL ON CACHE BOOL "")
+ set(OPENSSLDIR "/etc/ssl/")
+ add_subdirectory(libressl EXCLUDE_FROM_ALL)
+ target_include_directories(ssl INTERFACE ./libressl/include)
+ target_compile_definitions(ssl PRIVATE -DHAVE_INET_NTOP)
+ get_directory_property(OPENSSL_LIBRARIES
+ DIRECTORY libressl
+ DEFINITION OPENSSL_LIBS)
+ endif()
# httplib
add_library(httplib INTERFACE)
target_include_directories(httplib INTERFACE ./httplib)
target_compile_definitions(httplib INTERFACE -DCPPHTTPLIB_OPENSSL_SUPPORT)
target_link_libraries(httplib INTERFACE ${OPENSSL_LIBRARIES})
+ if (WIN32)
+ target_link_libraries(httplib INTERFACE crypt32 cryptui ws2_32)
+ endif()
endif()
# Opus
diff --git a/externals/find-modules/FindFFmpeg.cmake b/externals/find-modules/FindFFmpeg.cmake
new file mode 100644
index 000000000..77b331e00
--- /dev/null
+++ b/externals/find-modules/FindFFmpeg.cmake
@@ -0,0 +1,100 @@
+# - Try to find ffmpeg libraries (libavcodec, libavformat and libavutil)
+# Once done this will define
+#
+# FFMPEG_FOUND - system has ffmpeg or libav
+# FFMPEG_INCLUDE_DIR - the ffmpeg include directory
+# FFMPEG_LIBRARIES - Link these to use ffmpeg
+# FFMPEG_LIBAVCODEC
+# FFMPEG_LIBAVFORMAT
+# FFMPEG_LIBAVUTIL
+#
+# Copyright (c) 2008 Andreas Schneider <mail@cynapses.org>
+# Modified for other libraries by Lasse Kärkkäinen <tronic>
+# Modified for Hedgewars by Stepik777
+# Modified for FFmpeg-example Tuukka Pasanen 2018
+# Modified for yuzu toastUnlimted 2020
+#
+# Redistribution and use is allowed according to the terms of the New
+# BSD license.
+#
+
+include(FindPackageHandleStandardArgs)
+
+find_package_handle_standard_args(FFMPEG
+ FOUND_VAR FFMPEG_FOUND
+ REQUIRED_VARS
+ FFMPEG_LIBRARY
+ FFMPEG_INCLUDE_DIR
+ VERSION_VAR FFMPEG_VERSION
+)
+
+if(FFMPEG_LIBRARIES AND FFMPEG_INCLUDE_DIR)
+ # in cache already
+ set(FFMPEG_FOUND TRUE)
+else()
+ # use pkg-config to get the directories and then use these values
+ # in the FIND_PATH() and FIND_LIBRARY() calls
+ find_package(PkgConfig)
+ if(PKG_CONFIG_FOUND)
+ pkg_check_modules(_FFMPEG_AVCODEC libavcodec)
+ pkg_check_modules(_FFMPEG_AVUTIL libavutil)
+ pkg_check_modules(_FFMPEG_SWSCALE libswscale)
+ endif()
+
+ find_path(FFMPEG_AVCODEC_INCLUDE_DIR
+ NAMES libavcodec/avcodec.h
+ PATHS ${_FFMPEG_AVCODEC_INCLUDE_DIRS}
+ /usr/include
+ /usr/local/include
+ /opt/local/include
+ /sw/include
+ PATH_SUFFIXES ffmpeg libav)
+
+ find_library(FFMPEG_LIBAVCODEC
+ NAMES avcodec
+ PATHS ${_FFMPEG_AVCODEC_LIBRARY_DIRS}
+ /usr/lib
+ /usr/local/lib
+ /opt/local/lib
+ /sw/lib)
+
+ find_library(FFMPEG_LIBAVUTIL
+ NAMES avutil
+ PATHS ${_FFMPEG_AVUTIL_LIBRARY_DIRS}
+ /usr/lib
+ /usr/local/lib
+ /opt/local/lib
+ /sw/lib)
+
+ find_library(FFMPEG_LIBSWSCALE
+ NAMES swscale
+ PATHS ${_FFMPEG_SWSCALE_LIBRARY_DIRS}
+ /usr/lib
+ /usr/local/lib
+ /opt/local/lib
+ /sw/lib)
+
+ if(FFMPEG_LIBAVCODEC AND FFMPEG_LIBAVUTIL AND FFMPEG_LIBSWSCALE)
+ set(FFMPEG_FOUND TRUE)
+ endif()
+
+ if(FFMPEG_FOUND)
+ set(FFMPEG_INCLUDE_DIR ${FFMPEG_AVCODEC_INCLUDE_DIR})
+ set(FFMPEG_LIBRARIES
+ ${FFMPEG_LIBAVCODEC}
+ ${FFMPEG_LIBAVUTIL}
+ ${FFMPEG_LIBSWSCALE})
+ endif()
+
+ if(FFMPEG_FOUND)
+ if(NOT FFMPEG_FIND_QUIETLY)
+ message(STATUS
+ "Found FFMPEG or Libav: ${FFMPEG_LIBRARIES}, ${FFMPEG_INCLUDE_DIR}")
+ endif()
+ else()
+ if(FFMPEG_FIND_REQUIRED)
+ message(FATAL_ERROR
+ "Could not find libavcodec or libavutil or libswscale")
+ endif()
+ endif()
+endif()
diff --git a/externals/httplib/README.md b/externals/httplib/README.md
index 73037d297..1940e446c 100644
--- a/externals/httplib/README.md
+++ b/externals/httplib/README.md
@@ -1,4 +1,4 @@
-From https://github.com/yhirose/cpp-httplib/tree/fce8e6fefdab4ad48bc5b25c98e5ebfda4f3cf53
+From https://github.com/yhirose/cpp-httplib/tree/ff5677ad197947177c158fe857caff4f0e242045 with https://github.com/yhirose/cpp-httplib/pull/701
MIT License
diff --git a/externals/httplib/httplib.h b/externals/httplib/httplib.h
index e03842e6d..8982054e2 100644
--- a/externals/httplib/httplib.h
+++ b/externals/httplib/httplib.h
@@ -16,14 +16,18 @@
#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND 5
#endif
-#ifndef CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND
-#define CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND 0
-#endif
-
#ifndef CPPHTTPLIB_KEEPALIVE_MAX_COUNT
#define CPPHTTPLIB_KEEPALIVE_MAX_COUNT 5
#endif
+#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND
+#define CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND 300
+#endif
+
+#ifndef CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND
+#define CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND 0
+#endif
+
#ifndef CPPHTTPLIB_READ_TIMEOUT_SECOND
#define CPPHTTPLIB_READ_TIMEOUT_SECOND 5
#endif
@@ -32,6 +36,26 @@
#define CPPHTTPLIB_READ_TIMEOUT_USECOND 0
#endif
+#ifndef CPPHTTPLIB_WRITE_TIMEOUT_SECOND
+#define CPPHTTPLIB_WRITE_TIMEOUT_SECOND 5
+#endif
+
+#ifndef CPPHTTPLIB_WRITE_TIMEOUT_USECOND
+#define CPPHTTPLIB_WRITE_TIMEOUT_USECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_IDLE_INTERVAL_SECOND
+#define CPPHTTPLIB_IDLE_INTERVAL_SECOND 0
+#endif
+
+#ifndef CPPHTTPLIB_IDLE_INTERVAL_USECOND
+#ifdef _WIN32
+#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 10000
+#else
+#define CPPHTTPLIB_IDLE_INTERVAL_USECOND 0
+#endif
+#endif
+
#ifndef CPPHTTPLIB_REQUEST_URI_MAX_LENGTH
#define CPPHTTPLIB_REQUEST_URI_MAX_LENGTH 8192
#endif
@@ -41,16 +65,26 @@
#endif
#ifndef CPPHTTPLIB_PAYLOAD_MAX_LENGTH
-#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH (std::numeric_limits<size_t>::max())
+#define CPPHTTPLIB_PAYLOAD_MAX_LENGTH ((std::numeric_limits<size_t>::max)())
+#endif
+
+#ifndef CPPHTTPLIB_TCP_NODELAY
+#define CPPHTTPLIB_TCP_NODELAY false
#endif
#ifndef CPPHTTPLIB_RECV_BUFSIZ
#define CPPHTTPLIB_RECV_BUFSIZ size_t(4096u)
#endif
+#ifndef CPPHTTPLIB_COMPRESSION_BUFSIZ
+#define CPPHTTPLIB_COMPRESSION_BUFSIZ size_t(16384u)
+#endif
+
#ifndef CPPHTTPLIB_THREAD_POOL_COUNT
#define CPPHTTPLIB_THREAD_POOL_COUNT \
- (std::max(1u, std::thread::hardware_concurrency() - 1))
+ ((std::max)(8u, std::thread::hardware_concurrency() > 0 \
+ ? std::thread::hardware_concurrency() - 1 \
+ : 0))
#endif
/*
@@ -92,6 +126,8 @@ using ssize_t = int;
#include <io.h>
#include <winsock2.h>
+
+#include <wincrypt.h>
#include <ws2tcpip.h>
#ifndef WSA_FLAG_NO_HANDLE_INHERIT
@@ -100,6 +136,8 @@ using ssize_t = int;
#ifdef _MSC_VER
#pragma comment(lib, "ws2_32.lib")
+#pragma comment(lib, "crypt32.lib")
+#pragma comment(lib, "cryptui.lib")
#endif
#ifndef strcasecmp
@@ -118,6 +156,10 @@ using socket_t = SOCKET;
#include <ifaddrs.h>
#include <netdb.h>
#include <netinet/in.h>
+#ifdef __linux__
+#include <resolv.h>
+#endif
+#include <netinet/tcp.h>
#ifdef CPPHTTPLIB_USE_POLL
#include <poll.h>
#endif
@@ -131,20 +173,25 @@ using socket_t = int;
#define INVALID_SOCKET (-1)
#endif //_WIN32
+#include <algorithm>
#include <array>
#include <atomic>
#include <cassert>
+#include <cctype>
+#include <climits>
#include <condition_variable>
#include <errno.h>
#include <fcntl.h>
#include <fstream>
#include <functional>
+#include <iostream>
#include <list>
#include <map>
#include <memory>
#include <mutex>
#include <random>
#include <regex>
+#include <sstream>
#include <string>
#include <sys/stat.h>
#include <thread>
@@ -155,12 +202,17 @@ using socket_t = int;
#include <openssl/ssl.h>
#include <openssl/x509v3.h>
+#if defined(_WIN32) && defined(OPENSSL_USE_APPLINK)
+#include <openssl/applink.c>
+#endif
+
#include <iomanip>
+#include <iostream>
#include <sstream>
-// #if OPENSSL_VERSION_NUMBER < 0x1010100fL
-// #error Sorry, OpenSSL versions prior to 1.1.1 are not supported
-// #endif
+#if OPENSSL_VERSION_NUMBER < 0x1010100fL
+#error Sorry, OpenSSL versions prior to 1.1.1 are not supported
+#endif
#if OPENSSL_VERSION_NUMBER < 0x10100000L
#include <openssl/crypto.h>
@@ -174,6 +226,11 @@ inline const unsigned char *ASN1_STRING_get0_data(const ASN1_STRING *asn1) {
#include <zlib.h>
#endif
+#ifdef CPPHTTPLIB_BROTLI_SUPPORT
+#include <brotli/decode.h>
+#include <brotli/encode.h>
+#endif
+
/*
* Declaration
*/
@@ -181,6 +238,27 @@ namespace httplib {
namespace detail {
+/*
+ * Backport std::make_unique from C++14.
+ *
+ * NOTE: This code came up with the following stackoverflow post:
+ * https://stackoverflow.com/questions/10149840/c-arrays-and-make-unique
+ *
+ */
+
+template <class T, class... Args>
+typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type
+make_unique(Args &&... args) {
+ return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
+}
+
+template <class T>
+typename std::enable_if<std::is_array<T>::value, std::unique_ptr<T>>::type
+make_unique(std::size_t n) {
+ typedef typename std::remove_extent<T>::type RT;
+ return std::unique_ptr<T>(new RT[n]);
+}
+
struct ci {
bool operator()(const std::string &s1, const std::string &s2) const {
return std::lexicographical_compare(
@@ -212,7 +290,8 @@ using MultipartFormDataMap = std::multimap<std::string, MultipartFormData>;
class DataSink {
public:
- DataSink() = default;
+ DataSink() : os(&sb_), sb_(*this) {}
+
DataSink(const DataSink &) = delete;
DataSink &operator=(const DataSink &) = delete;
DataSink(DataSink &&) = delete;
@@ -221,10 +300,35 @@ public:
std::function<void(const char *data, size_t data_len)> write;
std::function<void()> done;
std::function<bool()> is_writable;
+ std::ostream os;
+
+private:
+ class data_sink_streambuf : public std::streambuf {
+ public:
+ explicit data_sink_streambuf(DataSink &sink) : sink_(sink) {}
+
+ protected:
+ std::streamsize xsputn(const char *s, std::streamsize n) {
+ sink_.write(s, static_cast<size_t>(n));
+ return n;
+ }
+
+ private:
+ DataSink &sink_;
+ };
+
+ data_sink_streambuf sb_;
};
using ContentProvider =
- std::function<void(size_t offset, size_t length, DataSink &sink)>;
+ std::function<bool(size_t offset, size_t length, DataSink &sink)>;
+
+using ContentProviderWithoutLength =
+ std::function<bool(size_t offset, DataSink &sink)>;
+
+using ContentReceiverWithProgress =
+ std::function<bool(const char *data, size_t data_length, uint64_t offset,
+ uint64_t total_length)>;
using ContentReceiver =
std::function<bool(const char *data, size_t data_length)>;
@@ -238,18 +342,21 @@ public:
using MultipartReader = std::function<bool(MultipartContentHeader header,
ContentReceiver receiver)>;
- ContentReader(Reader reader, MultipartReader muitlpart_reader)
- : reader_(reader), muitlpart_reader_(muitlpart_reader) {}
+ ContentReader(Reader reader, MultipartReader multipart_reader)
+ : reader_(std::move(reader)),
+ multipart_reader_(std::move(multipart_reader)) {}
bool operator()(MultipartContentHeader header,
ContentReceiver receiver) const {
- return muitlpart_reader_(header, receiver);
+ return multipart_reader_(std::move(header), std::move(receiver));
}
- bool operator()(ContentReceiver receiver) const { return reader_(receiver); }
+ bool operator()(ContentReceiver receiver) const {
+ return reader_(std::move(receiver));
+ }
Reader reader_;
- MultipartReader muitlpart_reader_;
+ MultipartReader multipart_reader_;
};
using Range = std::pair<ssize_t, ssize_t>;
@@ -261,6 +368,9 @@ struct Request {
Headers headers;
std::string body;
+ std::string remote_addr;
+ int remote_port = -1;
+
// for server
std::string version;
std::string target;
@@ -272,7 +382,9 @@ struct Request {
// for client
size_t redirect_count = CPPHTTPLIB_REDIRECT_MAX_COUNT;
ResponseHandler response_handler;
- ContentReceiver content_receiver;
+ ContentReceiverWithProgress content_receiver;
+ size_t content_length = 0;
+ ContentProvider content_provider;
Progress progress;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
@@ -281,6 +393,8 @@ struct Request {
bool has_header(const char *key) const;
std::string get_header_value(const char *key, size_t id = 0) const;
+ template <typename T>
+ T get_header_value(const char *key, size_t id = 0) const;
size_t get_header_value_count(const char *key) const;
void set_header(const char *key, const char *val);
void set_header(const char *key, const std::string &val);
@@ -295,35 +409,40 @@ struct Request {
MultipartFormData get_file_value(const char *key) const;
// private members...
- size_t content_length;
- ContentProvider content_provider;
+ size_t authorization_count_ = 0;
};
struct Response {
std::string version;
int status = -1;
+ std::string reason;
Headers headers;
std::string body;
bool has_header(const char *key) const;
std::string get_header_value(const char *key, size_t id = 0) const;
+ template <typename T>
+ T get_header_value(const char *key, size_t id = 0) const;
size_t get_header_value_count(const char *key) const;
void set_header(const char *key, const char *val);
void set_header(const char *key, const std::string &val);
- void set_redirect(const char *url);
+ void set_redirect(const char *url, int status = 302);
+ void set_redirect(const std::string &url, int status = 302);
void set_content(const char *s, size_t n, const char *content_type);
- void set_content(const std::string &s, const char *content_type);
+ void set_content(std::string s, const char *content_type);
void set_content_provider(
- size_t length,
- std::function<void(size_t offset, size_t length, DataSink &sink)>
- provider,
- std::function<void()> resource_releaser = [] {});
+ size_t length, const char *content_type, ContentProvider provider,
+ const std::function<void()> &resource_releaser = nullptr);
+
+ void set_content_provider(
+ const char *content_type, ContentProviderWithoutLength provider,
+ const std::function<void()> &resource_releaser = nullptr);
void set_chunked_content_provider(
- std::function<void(size_t offset, DataSink &sink)> provider,
- std::function<void()> resource_releaser = [] {});
+ const char *content_type, ContentProviderWithoutLength provider,
+ const std::function<void()> &resource_releaser = nullptr);
Response() = default;
Response(const Response &) = default;
@@ -331,15 +450,16 @@ struct Response {
Response(Response &&) = default;
Response &operator=(Response &&) = default;
~Response() {
- if (content_provider_resource_releaser) {
- content_provider_resource_releaser();
+ if (content_provider_resource_releaser_) {
+ content_provider_resource_releaser_();
}
}
// private members...
- size_t content_length = 0;
- ContentProvider content_provider;
- std::function<void()> content_provider_resource_releaser;
+ size_t content_length_ = 0;
+ ContentProvider content_provider_;
+ std::function<void()> content_provider_resource_releaser_;
+ bool is_chunked_content_provider = false;
};
class Stream {
@@ -349,22 +469,25 @@ public:
virtual bool is_readable() const = 0;
virtual bool is_writable() const = 0;
- virtual int read(char *ptr, size_t size) = 0;
- virtual int write(const char *ptr, size_t size) = 0;
- virtual std::string get_remote_addr() const = 0;
+ virtual ssize_t read(char *ptr, size_t size) = 0;
+ virtual ssize_t write(const char *ptr, size_t size) = 0;
+ virtual void get_remote_ip_and_port(std::string &ip, int &port) const = 0;
template <typename... Args>
- int write_format(const char *fmt, const Args &... args);
- int write(const char *ptr);
- int write(const std::string &s);
+ ssize_t write_format(const char *fmt, const Args &... args);
+ ssize_t write(const char *ptr);
+ ssize_t write(const std::string &s);
};
class TaskQueue {
public:
TaskQueue() = default;
virtual ~TaskQueue() = default;
+
virtual void enqueue(std::function<void()> fn) = 0;
virtual void shutdown() = 0;
+
+ virtual void on_idle(){};
};
class ThreadPool : public TaskQueue {
@@ -381,7 +504,7 @@ public:
void enqueue(std::function<void()> fn) override {
std::unique_lock<std::mutex> lock(mutex_);
- jobs_.push_back(fn);
+ jobs_.push_back(std::move(fn));
cond_.notify_one();
}
@@ -439,6 +562,26 @@ private:
using Logger = std::function<void(const Request &, const Response &)>;
+using SocketOptions = std::function<void(socket_t sock)>;
+
+inline void default_socket_options(socket_t sock) {
+ int yes = 1;
+#ifdef _WIN32
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
+ sizeof(yes));
+ setsockopt(sock, SOL_SOCKET, SO_EXCLUSIVEADDRUSE,
+ reinterpret_cast<char *>(&yes), sizeof(yes));
+#else
+#ifdef SO_REUSEPORT
+ setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<void *>(&yes),
+ sizeof(yes));
+#else
+ setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<void *>(&yes),
+ sizeof(yes));
+#endif
+#endif
+}
+
class Server {
public:
using Handler = std::function<void(const Request &, Response &)>;
@@ -461,23 +604,30 @@ public:
Server &Patch(const char *pattern, Handler handler);
Server &Patch(const char *pattern, HandlerWithContentReader handler);
Server &Delete(const char *pattern, Handler handler);
+ Server &Delete(const char *pattern, HandlerWithContentReader handler);
Server &Options(const char *pattern, Handler handler);
- [[deprecated]] bool set_base_dir(const char *dir,
- const char *mount_point = nullptr);
- bool set_mount_point(const char *mount_point, const char *dir);
+ bool set_base_dir(const char *dir, const char *mount_point = nullptr);
+ bool set_mount_point(const char *mount_point, const char *dir,
+ Headers headers = Headers());
bool remove_mount_point(const char *mount_point);
void set_file_extension_and_mimetype_mapping(const char *ext,
const char *mime);
void set_file_request_handler(Handler handler);
void set_error_handler(Handler handler);
+ void set_expect_100_continue_handler(Expect100ContinueHandler handler);
void set_logger(Logger logger);
- void set_expect_100_continue_handler(Expect100ContinueHandler handler);
+ void set_tcp_nodelay(bool on);
+ void set_socket_options(SocketOptions socket_options);
void set_keep_alive_max_count(size_t count);
- void set_read_timeout(time_t sec, time_t usec);
+ void set_keep_alive_timeout(time_t sec);
+ void set_read_timeout(time_t sec, time_t usec = 0);
+ void set_write_timeout(time_t sec, time_t usec = 0);
+ void set_idle_interval(time_t sec, time_t usec = 0);
+
void set_payload_max_length(size_t length);
bool bind_to_port(const char *host, int port, int socket_flags = 0);
@@ -492,54 +642,66 @@ public:
std::function<TaskQueue *(void)> new_task_queue;
protected:
- bool process_request(Stream &strm, bool last_connection,
- bool &connection_close,
+ bool process_request(Stream &strm, bool close_connection,
+ bool &connection_closed,
const std::function<void(Request &)> &setup_request);
- size_t keep_alive_max_count_;
- time_t read_timeout_sec_;
- time_t read_timeout_usec_;
- size_t payload_max_length_;
+ std::atomic<socket_t> svr_sock_;
+ size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
+ time_t keep_alive_timeout_sec_ = CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND;
+ time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
+ time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
+ time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
+ time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
+ time_t idle_interval_sec_ = CPPHTTPLIB_IDLE_INTERVAL_SECOND;
+ time_t idle_interval_usec_ = CPPHTTPLIB_IDLE_INTERVAL_USECOND;
+ size_t payload_max_length_ = CPPHTTPLIB_PAYLOAD_MAX_LENGTH;
private:
using Handlers = std::vector<std::pair<std::regex, Handler>>;
using HandlersForContentReader =
std::vector<std::pair<std::regex, HandlerWithContentReader>>;
- socket_t create_server_socket(const char *host, int port,
- int socket_flags) const;
+ socket_t create_server_socket(const char *host, int port, int socket_flags,
+ SocketOptions socket_options) const;
int bind_internal(const char *host, int port, int socket_flags);
bool listen_internal();
- bool routing(Request &req, Response &res, Stream &strm, bool last_connection);
+ bool routing(Request &req, Response &res, Stream &strm);
bool handle_file_request(Request &req, Response &res, bool head = false);
- bool dispatch_request(Request &req, Response &res, Handlers &handlers);
- bool dispatch_request_for_content_reader(Request &req, Response &res,
- ContentReader content_reader,
- HandlersForContentReader &handlers);
+ bool dispatch_request(Request &req, Response &res, const Handlers &handlers);
+ bool
+ dispatch_request_for_content_reader(Request &req, Response &res,
+ ContentReader content_reader,
+ const HandlersForContentReader &handlers);
bool parse_request_line(const char *s, Request &req);
- bool write_response(Stream &strm, bool last_connection, const Request &req,
+ bool write_response(Stream &strm, bool close_connection, const Request &req,
Response &res);
bool write_content_with_provider(Stream &strm, const Request &req,
Response &res, const std::string &boundary,
const std::string &content_type);
- bool read_content(Stream &strm, bool last_connection, Request &req,
- Response &res);
- bool read_content_with_content_receiver(
- Stream &strm, bool last_connection, Request &req, Response &res,
- ContentReceiver receiver, MultipartContentHeader multipart_header,
- ContentReceiver multipart_receiver);
- bool read_content_core(Stream &strm, bool last_connection, Request &req,
- Response &res, ContentReceiver receiver,
+ bool read_content(Stream &strm, Request &req, Response &res);
+ bool
+ read_content_with_content_receiver(Stream &strm, Request &req, Response &res,
+ ContentReceiver receiver,
+ MultipartContentHeader multipart_header,
+ ContentReceiver multipart_receiver);
+ bool read_content_core(Stream &strm, Request &req, Response &res,
+ ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
ContentReceiver multipart_receiver);
virtual bool process_and_close_socket(socket_t sock);
+ struct MountPointEntry {
+ std::string mount_point;
+ std::string base_dir;
+ Headers headers;
+ };
+ std::vector<MountPointEntry> base_dirs_;
+
std::atomic<bool> is_running_;
- std::atomic<socket_t> svr_sock_;
- std::vector<std::pair<std::string, std::string>> base_dirs_;
std::map<std::string, std::string> file_extension_and_mimetype_map_;
Handler file_request_handler_;
Handlers get_handlers_;
@@ -550,293 +712,454 @@ private:
Handlers patch_handlers_;
HandlersForContentReader patch_handlers_for_content_reader_;
Handlers delete_handlers_;
+ HandlersForContentReader delete_handlers_for_content_reader_;
Handlers options_handlers_;
Handler error_handler_;
Logger logger_;
Expect100ContinueHandler expect_100_continue_handler_;
-};
-
-class Client {
-public:
- explicit Client(const std::string &host, int port = 80,
- const std::string &client_cert_path = std::string(),
- const std::string &client_key_path = std::string());
- virtual ~Client();
-
- virtual bool is_valid() const;
-
- std::shared_ptr<Response> Get(const char *path);
-
- std::shared_ptr<Response> Get(const char *path, const Headers &headers);
-
- std::shared_ptr<Response> Get(const char *path, Progress progress);
-
- std::shared_ptr<Response> Get(const char *path, const Headers &headers,
- Progress progress);
-
- std::shared_ptr<Response> Get(const char *path,
- ContentReceiver content_receiver);
-
- std::shared_ptr<Response> Get(const char *path, const Headers &headers,
- ContentReceiver content_receiver);
-
- std::shared_ptr<Response>
- Get(const char *path, ContentReceiver content_receiver, Progress progress);
-
- std::shared_ptr<Response> Get(const char *path, const Headers &headers,
- ContentReceiver content_receiver,
- Progress progress);
-
- std::shared_ptr<Response> Get(const char *path, const Headers &headers,
- ResponseHandler response_handler,
- ContentReceiver content_receiver);
-
- std::shared_ptr<Response> Get(const char *path, const Headers &headers,
- ResponseHandler response_handler,
- ContentReceiver content_receiver,
- Progress progress);
-
- std::shared_ptr<Response> Head(const char *path);
-
- std::shared_ptr<Response> Head(const char *path, const Headers &headers);
-
- std::shared_ptr<Response> Post(const char *path, const std::string &body,
- const char *content_type);
-
- std::shared_ptr<Response> Post(const char *path, const Headers &headers,
- const std::string &body,
- const char *content_type);
-
- std::shared_ptr<Response> Post(const char *path, size_t content_length,
- ContentProvider content_provider,
- const char *content_type);
-
- std::shared_ptr<Response> Post(const char *path, const Headers &headers,
- size_t content_length,
- ContentProvider content_provider,
- const char *content_type);
-
- std::shared_ptr<Response> Post(const char *path, const Params &params);
-
- std::shared_ptr<Response> Post(const char *path, const Headers &headers,
- const Params &params);
-
- std::shared_ptr<Response> Post(const char *path,
- const MultipartFormDataItems &items);
-
- std::shared_ptr<Response> Post(const char *path, const Headers &headers,
- const MultipartFormDataItems &items);
-
- std::shared_ptr<Response> Put(const char *path, const std::string &body,
- const char *content_type);
-
- std::shared_ptr<Response> Put(const char *path, const Headers &headers,
- const std::string &body,
- const char *content_type);
-
- std::shared_ptr<Response> Put(const char *path, size_t content_length,
- ContentProvider content_provider,
- const char *content_type);
-
- std::shared_ptr<Response> Put(const char *path, const Headers &headers,
- size_t content_length,
- ContentProvider content_provider,
- const char *content_type);
-
- std::shared_ptr<Response> Put(const char *path, const Params &params);
-
- std::shared_ptr<Response> Put(const char *path, const Headers &headers,
- const Params &params);
-
- std::shared_ptr<Response> Patch(const char *path, const std::string &body,
- const char *content_type);
+ bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
+ SocketOptions socket_options_ = default_socket_options;
+};
- std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
- const std::string &body,
- const char *content_type);
+enum Error {
+ Success = 0,
+ Unknown,
+ Connection,
+ BindIPAddress,
+ Read,
+ Write,
+ ExceedRedirectCount,
+ Canceled,
+ SSLConnection,
+ SSLLoadingCerts,
+ SSLServerVerification,
+ UnsupportedMultipartBoundaryChars
+};
- std::shared_ptr<Response> Patch(const char *path, size_t content_length,
- ContentProvider content_provider,
- const char *content_type);
+class Result {
+public:
+ Result(std::unique_ptr<Response> res, Error err)
+ : res_(std::move(res)), err_(err) {}
+ operator bool() const { return res_ != nullptr; }
+ bool operator==(std::nullptr_t) const { return res_ == nullptr; }
+ bool operator!=(std::nullptr_t) const { return res_ != nullptr; }
+ const Response &value() const { return *res_; }
+ Response &value() { return *res_; }
+ const Response &operator*() const { return *res_; }
+ Response &operator*() { return *res_; }
+ const Response *operator->() const { return res_.get(); }
+ Response *operator->() { return res_.get(); }
+ Error error() const { return err_; }
- std::shared_ptr<Response> Patch(const char *path, const Headers &headers,
- size_t content_length,
- ContentProvider content_provider,
- const char *content_type);
+private:
+ std::unique_ptr<Response> res_;
+ Error err_;
+};
- std::shared_ptr<Response> Delete(const char *path);
+class ClientImpl {
+public:
+ explicit ClientImpl(const std::string &host);
- std::shared_ptr<Response> Delete(const char *path, const std::string &body,
- const char *content_type);
+ explicit ClientImpl(const std::string &host, int port);
- std::shared_ptr<Response> Delete(const char *path, const Headers &headers);
+ explicit ClientImpl(const std::string &host, int port,
+ const std::string &client_cert_path,
+ const std::string &client_key_path);
- std::shared_ptr<Response> Delete(const char *path, const Headers &headers,
- const std::string &body,
- const char *content_type);
+ virtual ~ClientImpl();
- std::shared_ptr<Response> Options(const char *path);
+ virtual bool is_valid() const;
- std::shared_ptr<Response> Options(const char *path, const Headers &headers);
+ Result Get(const char *path);
+ Result Get(const char *path, const Headers &headers);
+ Result Get(const char *path, Progress progress);
+ Result Get(const char *path, const Headers &headers, Progress progress);
+ Result Get(const char *path, ContentReceiver content_receiver);
+ Result Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver);
+ Result Get(const char *path, ContentReceiver content_receiver,
+ Progress progress);
+ Result Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver, Progress progress);
+ Result Get(const char *path, ResponseHandler response_handler,
+ ContentReceiver content_receiver);
+ Result Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver);
+ Result Get(const char *path, ResponseHandler response_handler,
+ ContentReceiver content_receiver, Progress progress);
+ Result Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler, ContentReceiver content_receiver,
+ Progress progress);
+
+ Result Head(const char *path);
+ Result Head(const char *path, const Headers &headers);
+
+ Result Post(const char *path);
+ Result Post(const char *path, const std::string &body,
+ const char *content_type);
+ Result Post(const char *path, const Headers &headers, const std::string &body,
+ const char *content_type);
+ Result Post(const char *path, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Post(const char *path, const Headers &headers, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Post(const char *path, const Params &params);
+ Result Post(const char *path, const Headers &headers, const Params &params);
+ Result Post(const char *path, const MultipartFormDataItems &items);
+ Result Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items);
+ Result Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items, const std::string &boundary);
+
+ Result Put(const char *path);
+ Result Put(const char *path, const std::string &body,
+ const char *content_type);
+ Result Put(const char *path, const Headers &headers, const std::string &body,
+ const char *content_type);
+ Result Put(const char *path, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Put(const char *path, const Headers &headers, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Put(const char *path, const Params &params);
+ Result Put(const char *path, const Headers &headers, const Params &params);
+
+ Result Patch(const char *path, const std::string &body,
+ const char *content_type);
+ Result Patch(const char *path, const Headers &headers,
+ const std::string &body, const char *content_type);
+ Result Patch(const char *path, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Patch(const char *path, const Headers &headers, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+
+ Result Delete(const char *path);
+ Result Delete(const char *path, const std::string &body,
+ const char *content_type);
+ Result Delete(const char *path, const Headers &headers);
+ Result Delete(const char *path, const Headers &headers,
+ const std::string &body, const char *content_type);
+
+ Result Options(const char *path);
+ Result Options(const char *path, const Headers &headers);
bool send(const Request &req, Response &res);
- bool send(const std::vector<Request> &requests,
- std::vector<Response> &responses);
+ size_t is_socket_open() const;
- void set_timeout_sec(time_t timeout_sec);
+ void stop();
- void set_read_timeout(time_t sec, time_t usec);
+ void set_default_headers(Headers headers);
- void set_keep_alive_max_count(size_t count);
+ void set_tcp_nodelay(bool on);
+ void set_socket_options(SocketOptions socket_options);
- void set_basic_auth(const char *username, const char *password);
+ void set_connection_timeout(time_t sec, time_t usec = 0);
+ void set_read_timeout(time_t sec, time_t usec = 0);
+ void set_write_timeout(time_t sec, time_t usec = 0);
+ void set_basic_auth(const char *username, const char *password);
+ void set_bearer_token_auth(const char *token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void set_digest_auth(const char *username, const char *password);
#endif
+ void set_keep_alive(bool on);
void set_follow_location(bool on);
void set_compress(bool on);
+ void set_decompress(bool on);
+
void set_interface(const char *intf);
void set_proxy(const char *host, int port);
-
void set_proxy_basic_auth(const char *username, const char *password);
-
+ void set_proxy_bearer_token_auth(const char *token);
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
void set_proxy_digest_auth(const char *username, const char *password);
#endif
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ void enable_server_certificate_verification(bool enabled);
+#endif
+
void set_logger(Logger logger);
protected:
+ struct Socket {
+ socket_t sock = INVALID_SOCKET;
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ SSL *ssl = nullptr;
+#endif
+
+ bool is_open() const { return sock != INVALID_SOCKET; }
+ };
+
+ virtual bool create_and_connect_socket(Socket &socket);
+
+ // All of:
+ // shutdown_ssl
+ // shutdown_socket
+ // close_socket
+ // should ONLY be called when socket_mutex_ is locked.
+ // Also, shutdown_ssl and close_socket should also NOT be called concurrently
+ // with a DIFFERENT thread sending requests using that socket.
+ virtual void shutdown_ssl(Socket &socket, bool shutdown_gracefully);
+ void shutdown_socket(Socket &socket);
+ void close_socket(Socket &socket);
+
+ // Similar to shutdown_ssl and close_socket, this should NOT be called
+ // concurrently with a DIFFERENT thread sending requests from the socket
+ void lock_socket_and_shutdown_and_close();
+
bool process_request(Stream &strm, const Request &req, Response &res,
- bool last_connection, bool &connection_close);
+ bool close_connection);
+
+ Error get_last_error() const;
+
+ void copy_settings(const ClientImpl &rhs);
+ // Error state
+ mutable std::atomic<Error> error_;
+
+ // Socket endoint information
const std::string host_;
const int port_;
const std::string host_and_port_;
+ // Current open socket
+ Socket socket_;
+ mutable std::mutex socket_mutex_;
+ std::recursive_mutex request_mutex_;
+
+ // These are all protected under socket_mutex
+ int socket_requests_in_flight_ = 0;
+ std::thread::id socket_requests_are_from_thread_ = std::thread::id();
+ bool socket_should_be_closed_when_request_is_done_ = false;
+
+ // Default headers
+ Headers default_headers_;
+
// Settings
std::string client_cert_path_;
std::string client_key_path_;
- time_t timeout_sec_ = 300;
+ time_t connection_timeout_sec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_SECOND;
+ time_t connection_timeout_usec_ = CPPHTTPLIB_CONNECTION_TIMEOUT_USECOND;
time_t read_timeout_sec_ = CPPHTTPLIB_READ_TIMEOUT_SECOND;
time_t read_timeout_usec_ = CPPHTTPLIB_READ_TIMEOUT_USECOND;
-
- size_t keep_alive_max_count_ = CPPHTTPLIB_KEEPALIVE_MAX_COUNT;
+ time_t write_timeout_sec_ = CPPHTTPLIB_WRITE_TIMEOUT_SECOND;
+ time_t write_timeout_usec_ = CPPHTTPLIB_WRITE_TIMEOUT_USECOND;
std::string basic_auth_username_;
std::string basic_auth_password_;
+ std::string bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
std::string digest_auth_username_;
std::string digest_auth_password_;
#endif
+ bool keep_alive_ = false;
bool follow_location_ = false;
+ bool tcp_nodelay_ = CPPHTTPLIB_TCP_NODELAY;
+ SocketOptions socket_options_ = nullptr;
+
bool compress_ = false;
+ bool decompress_ = true;
std::string interface_;
std::string proxy_host_;
- int proxy_port_;
+ int proxy_port_ = -1;
std::string proxy_basic_auth_username_;
std::string proxy_basic_auth_password_;
+ std::string proxy_bearer_token_auth_token_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
std::string proxy_digest_auth_username_;
std::string proxy_digest_auth_password_;
#endif
- Logger logger_;
-
- void copy_settings(const Client &rhs) {
- client_cert_path_ = rhs.client_cert_path_;
- client_key_path_ = rhs.client_key_path_;
- timeout_sec_ = rhs.timeout_sec_;
- read_timeout_sec_ = rhs.read_timeout_sec_;
- read_timeout_usec_ = rhs.read_timeout_usec_;
- keep_alive_max_count_ = rhs.keep_alive_max_count_;
- basic_auth_username_ = rhs.basic_auth_username_;
- basic_auth_password_ = rhs.basic_auth_password_;
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- digest_auth_username_ = rhs.digest_auth_username_;
- digest_auth_password_ = rhs.digest_auth_password_;
-#endif
- follow_location_ = rhs.follow_location_;
- compress_ = rhs.compress_;
- interface_ = rhs.interface_;
- proxy_host_ = rhs.proxy_host_;
- proxy_port_ = rhs.proxy_port_;
- proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
- proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
- proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
+ bool server_certificate_verification_ = true;
#endif
- logger_ = rhs.logger_;
- }
+
+ Logger logger_;
private:
socket_t create_client_socket() const;
bool read_response_line(Stream &strm, Response &res);
- bool write_request(Stream &strm, const Request &req, bool last_connection);
+ bool write_request(Stream &strm, const Request &req, bool close_connection);
bool redirect(const Request &req, Response &res);
bool handle_request(Stream &strm, const Request &req, Response &res,
- bool last_connection, bool &connection_close);
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- bool connect(socket_t sock, Response &res, bool &error);
-#endif
-
- std::shared_ptr<Response> send_with_content_provider(
+ bool close_connection);
+ std::unique_ptr<Response> send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider, const char *content_type);
- virtual bool process_and_close_socket(
- socket_t sock, size_t request_count,
- std::function<bool(Stream &strm, bool last_connection,
- bool &connection_close)>
- callback);
-
+ // socket is const because this function is called when socket_mutex_ is not locked
+ virtual bool process_socket(const Socket &socket,
+ std::function<bool(Stream &strm)> callback);
virtual bool is_ssl() const;
};
-inline void Get(std::vector<Request> &requests, const char *path,
- const Headers &headers) {
- Request req;
- req.method = "GET";
- req.path = path;
- req.headers = headers;
- requests.emplace_back(std::move(req));
-}
+class Client {
+public:
+ // Universal interface
+ explicit Client(const char *scheme_host_port);
+
+ explicit Client(const char *scheme_host_port,
+ const std::string &client_cert_path,
+ const std::string &client_key_path);
+
+ // HTTP only interface
+ explicit Client(const std::string &host, int port);
+
+ explicit Client(const std::string &host, int port,
+ const std::string &client_cert_path,
+ const std::string &client_key_path);
+
+ ~Client();
+
+ bool is_valid() const;
+
+ Result Get(const char *path);
+ Result Get(const char *path, const Headers &headers);
+ Result Get(const char *path, Progress progress);
+ Result Get(const char *path, const Headers &headers, Progress progress);
+ Result Get(const char *path, ContentReceiver content_receiver);
+ Result Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver);
+ Result Get(const char *path, ContentReceiver content_receiver,
+ Progress progress);
+ Result Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver, Progress progress);
+ Result Get(const char *path, ResponseHandler response_handler,
+ ContentReceiver content_receiver);
+ Result Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver);
+ Result Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler, ContentReceiver content_receiver,
+ Progress progress);
+ Result Get(const char *path, ResponseHandler response_handler,
+ ContentReceiver content_receiver, Progress progress);
+
+ Result Head(const char *path);
+ Result Head(const char *path, const Headers &headers);
+
+ Result Post(const char *path);
+ Result Post(const char *path, const std::string &body,
+ const char *content_type);
+ Result Post(const char *path, const Headers &headers, const std::string &body,
+ const char *content_type);
+ Result Post(const char *path, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Post(const char *path, const Headers &headers, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Post(const char *path, const Params &params);
+ Result Post(const char *path, const Headers &headers, const Params &params);
+ Result Post(const char *path, const MultipartFormDataItems &items);
+ Result Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items);
+ Result Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items, const std::string &boundary);
+ Result Put(const char *path);
+ Result Put(const char *path, const std::string &body,
+ const char *content_type);
+ Result Put(const char *path, const Headers &headers, const std::string &body,
+ const char *content_type);
+ Result Put(const char *path, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Put(const char *path, const Headers &headers, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Put(const char *path, const Params &params);
+ Result Put(const char *path, const Headers &headers, const Params &params);
+ Result Patch(const char *path, const std::string &body,
+ const char *content_type);
+ Result Patch(const char *path, const Headers &headers,
+ const std::string &body, const char *content_type);
+ Result Patch(const char *path, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+ Result Patch(const char *path, const Headers &headers, size_t content_length,
+ ContentProvider content_provider, const char *content_type);
+
+ Result Delete(const char *path);
+ Result Delete(const char *path, const std::string &body,
+ const char *content_type);
+ Result Delete(const char *path, const Headers &headers);
+ Result Delete(const char *path, const Headers &headers,
+ const std::string &body, const char *content_type);
+
+ Result Options(const char *path);
+ Result Options(const char *path, const Headers &headers);
-inline void Get(std::vector<Request> &requests, const char *path) {
- Get(requests, path, Headers());
-}
+ bool send(const Request &req, Response &res);
-inline void Post(std::vector<Request> &requests, const char *path,
- const Headers &headers, const std::string &body,
- const char *content_type) {
- Request req;
- req.method = "POST";
- req.path = path;
- req.headers = headers;
- req.headers.emplace("Content-Type", content_type);
- req.body = body;
- requests.emplace_back(std::move(req));
-}
+ size_t is_socket_open() const;
-inline void Post(std::vector<Request> &requests, const char *path,
- const std::string &body, const char *content_type) {
- Post(requests, path, Headers(), body, content_type);
-}
+ void stop();
+
+ void set_default_headers(Headers headers);
+
+ void set_tcp_nodelay(bool on);
+ void set_socket_options(SocketOptions socket_options);
+
+ void set_connection_timeout(time_t sec, time_t usec = 0);
+ void set_read_timeout(time_t sec, time_t usec = 0);
+ void set_write_timeout(time_t sec, time_t usec = 0);
+
+ void set_basic_auth(const char *username, const char *password);
+ void set_bearer_token_auth(const char *token);
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ void set_digest_auth(const char *username, const char *password);
+#endif
+
+ void set_keep_alive(bool on);
+ void set_follow_location(bool on);
+
+ void set_compress(bool on);
+
+ void set_decompress(bool on);
+
+ void set_interface(const char *intf);
+
+ void set_proxy(const char *host, int port);
+ void set_proxy_basic_auth(const char *username, const char *password);
+ void set_proxy_bearer_token_auth(const char *token);
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ void set_proxy_digest_auth(const char *username, const char *password);
+#endif
+
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ void enable_server_certificate_verification(bool enabled);
+#endif
+
+ void set_logger(Logger logger);
+
+ // SSL
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ void set_ca_cert_path(const char *ca_cert_file_path,
+ const char *ca_cert_dir_path = nullptr);
+
+ void set_ca_cert_store(X509_STORE *ca_cert_store);
+
+ long get_openssl_verify_result() const;
+
+ SSL_CTX *ssl_context() const;
+#endif
+
+private:
+ std::unique_ptr<ClientImpl> cli_;
+
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ bool is_ssl_ = false;
+#endif
+}; // namespace httplib
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
class SSLServer : public Server {
@@ -845,43 +1168,58 @@ public:
const char *client_ca_cert_file_path = nullptr,
const char *client_ca_cert_dir_path = nullptr);
- virtual ~SSLServer();
+ SSLServer(X509 *cert, EVP_PKEY *private_key,
+ X509_STORE *client_ca_cert_store = nullptr);
- virtual bool is_valid() const;
+ ~SSLServer() override;
+
+ bool is_valid() const override;
private:
- virtual bool process_and_close_socket(socket_t sock);
+ bool process_and_close_socket(socket_t sock) override;
SSL_CTX *ctx_;
std::mutex ctx_mutex_;
};
-class SSLClient : public Client {
+class SSLClient : public ClientImpl {
public:
- SSLClient(const std::string &host, int port = 443,
- const std::string &client_cert_path = std::string(),
- const std::string &client_key_path = std::string());
+ explicit SSLClient(const std::string &host);
- virtual ~SSLClient();
+ explicit SSLClient(const std::string &host, int port);
- virtual bool is_valid() const;
+ explicit SSLClient(const std::string &host, int port,
+ const std::string &client_cert_path,
+ const std::string &client_key_path);
+
+ explicit SSLClient(const std::string &host, int port, X509 *client_cert,
+ EVP_PKEY *client_key);
+
+ ~SSLClient() override;
- void set_ca_cert_path(const char *ca_ceert_file_path,
+ bool is_valid() const override;
+
+ void set_ca_cert_path(const char *ca_cert_file_path,
const char *ca_cert_dir_path = nullptr);
- void enable_server_certificate_verification(bool enabled);
+ void set_ca_cert_store(X509_STORE *ca_cert_store);
long get_openssl_verify_result() const;
- SSL_CTX *ssl_context() const noexcept;
+ SSL_CTX *ssl_context() const;
private:
- virtual bool process_and_close_socket(
- socket_t sock, size_t request_count,
- std::function<bool(Stream &strm, bool last_connection,
- bool &connection_close)>
- callback);
- virtual bool is_ssl() const;
+ bool create_and_connect_socket(Socket &socket) override;
+ void shutdown_ssl(Socket &socket, bool shutdown_gracefully) override;
+
+ bool process_socket(const Socket &socket,
+ std::function<bool(Stream &strm)> callback) override;
+ bool is_ssl() const override;
+
+ bool connect_with_proxy(Socket &sock, Response &res, bool &success);
+ bool initialize_ssl(Socket &socket);
+
+ bool load_certs();
bool verify_host(X509 *server_cert) const;
bool verify_host_with_subject_alt_name(X509 *server_cert) const;
@@ -890,12 +1228,15 @@ private:
SSL_CTX *ctx_;
std::mutex ctx_mutex_;
+ std::once_flag initialize_cert_;
+
std::vector<std::string> host_components_;
std::string ca_cert_file_path_;
std::string ca_cert_dir_path_;
- bool server_certificate_verification_ = false;
long verify_result_ = 0;
+
+ friend class ClientImpl;
};
#endif
@@ -948,31 +1289,39 @@ inline std::string from_i_to_hex(size_t n) {
return ret;
}
+inline bool start_with(const std::string &a, const std::string &b) {
+ if (a.size() < b.size()) { return false; }
+ for (size_t i = 0; i < b.size(); i++) {
+ if (::tolower(a[i]) != ::tolower(b[i])) { return false; }
+ }
+ return true;
+}
+
inline size_t to_utf8(int code, char *buff) {
if (code < 0x0080) {
buff[0] = (code & 0x7F);
return 1;
} else if (code < 0x0800) {
- buff[0] = (0xC0 | ((code >> 6) & 0x1F));
- buff[1] = (0x80 | (code & 0x3F));
+ buff[0] = static_cast<char>(0xC0 | ((code >> 6) & 0x1F));
+ buff[1] = static_cast<char>(0x80 | (code & 0x3F));
return 2;
} else if (code < 0xD800) {
- buff[0] = (0xE0 | ((code >> 12) & 0xF));
- buff[1] = (0x80 | ((code >> 6) & 0x3F));
- buff[2] = (0x80 | (code & 0x3F));
+ buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
+ buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
+ buff[2] = static_cast<char>(0x80 | (code & 0x3F));
return 3;
} else if (code < 0xE000) { // D800 - DFFF is invalid...
return 0;
} else if (code < 0x10000) {
- buff[0] = (0xE0 | ((code >> 12) & 0xF));
- buff[1] = (0x80 | ((code >> 6) & 0x3F));
- buff[2] = (0x80 | (code & 0x3F));
+ buff[0] = static_cast<char>(0xE0 | ((code >> 12) & 0xF));
+ buff[1] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
+ buff[2] = static_cast<char>(0x80 | (code & 0x3F));
return 3;
} else if (code < 0x110000) {
- buff[0] = (0xF0 | ((code >> 18) & 0x7));
- buff[1] = (0x80 | ((code >> 12) & 0x3F));
- buff[2] = (0x80 | ((code >> 6) & 0x3F));
- buff[3] = (0x80 | (code & 0x3F));
+ buff[0] = static_cast<char>(0xF0 | ((code >> 18) & 0x7));
+ buff[1] = static_cast<char>(0x80 | ((code >> 12) & 0x3F));
+ buff[2] = static_cast<char>(0x80 | ((code >> 6) & 0x3F));
+ buff[3] = static_cast<char>(0x80 | (code & 0x3F));
return 4;
}
@@ -992,8 +1341,8 @@ inline std::string base64_encode(const std::string &in) {
int val = 0;
int valb = -6;
- for (uint8_t c : in) {
- val = (val << 8) + c;
+ for (auto c : in) {
+ val = (val << 8) + static_cast<uint8_t>(c);
valb += 8;
while (valb >= 0) {
out.push_back(lookup[(val >> valb) & 0x3F]);
@@ -1057,13 +1406,81 @@ inline bool is_valid_path(const std::string &path) {
return true;
}
+inline std::string encode_url(const std::string &s) {
+ std::string result;
+
+ for (size_t i = 0; s[i]; i++) {
+ switch (s[i]) {
+ case ' ': result += "%20"; break;
+ case '+': result += "%2B"; break;
+ case '\r': result += "%0D"; break;
+ case '\n': result += "%0A"; break;
+ case '\'': result += "%27"; break;
+ case ',': result += "%2C"; break;
+ // case ':': result += "%3A"; break; // ok? probably...
+ case ';': result += "%3B"; break;
+ default:
+ auto c = static_cast<uint8_t>(s[i]);
+ if (c >= 0x80) {
+ result += '%';
+ char hex[4];
+ auto len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
+ assert(len == 2);
+ result.append(hex, static_cast<size_t>(len));
+ } else {
+ result += s[i];
+ }
+ break;
+ }
+ }
+
+ return result;
+}
+
+inline std::string decode_url(const std::string &s,
+ bool convert_plus_to_space) {
+ std::string result;
+
+ for (size_t i = 0; i < s.size(); i++) {
+ if (s[i] == '%' && i + 1 < s.size()) {
+ if (s[i + 1] == 'u') {
+ int val = 0;
+ if (from_hex_to_i(s, i + 2, 4, val)) {
+ // 4 digits Unicode codes
+ char buff[4];
+ size_t len = to_utf8(val, buff);
+ if (len > 0) { result.append(buff, len); }
+ i += 5; // 'u0000'
+ } else {
+ result += s[i];
+ }
+ } else {
+ int val = 0;
+ if (from_hex_to_i(s, i + 1, 2, val)) {
+ // 2 digits hex codes
+ result += static_cast<char>(val);
+ i += 2; // '00'
+ } else {
+ result += s[i];
+ }
+ }
+ } else if (convert_plus_to_space && s[i] == '+') {
+ result += ' ';
+ } else {
+ result += s[i];
+ }
+ }
+
+ return result;
+}
+
inline void read_file(const std::string &path, std::string &out) {
std::ifstream fs(path, std::ios_base::binary);
fs.seekg(0, std::ios_base::end);
auto size = fs.tellg();
fs.seekg(0);
out.resize(static_cast<size_t>(size));
- fs.read(&out[0], size);
+ fs.read(&out[0], static_cast<std::streamsize>(size));
}
inline std::string file_extension(const std::string &path) {
@@ -1073,19 +1490,41 @@ inline std::string file_extension(const std::string &path) {
return std::string();
}
+inline bool is_space_or_tab(char c) { return c == ' ' || c == '\t'; }
+
+inline std::pair<size_t, size_t> trim(const char *b, const char *e, size_t left,
+ size_t right) {
+ while (b + left < e && is_space_or_tab(b[left])) {
+ left++;
+ }
+ while (right > 0 && is_space_or_tab(b[right - 1])) {
+ right--;
+ }
+ return std::make_pair(left, right);
+}
+
+inline std::string trim_copy(const std::string &s) {
+ auto r = trim(s.data(), s.data() + s.size(), 0, s.size());
+ return s.substr(r.first, r.second - r.first);
+}
+
template <class Fn> void split(const char *b, const char *e, char d, Fn fn) {
- int i = 0;
- int beg = 0;
+ size_t i = 0;
+ size_t beg = 0;
- while (e ? (b + i != e) : (b[i] != '\0')) {
+ while (e ? (b + i < e) : (b[i] != '\0')) {
if (b[i] == d) {
- fn(&b[beg], &b[i]);
+ auto r = trim(b, e, beg, i);
+ if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
beg = i + 1;
}
i++;
}
- if (i) { fn(&b[beg], &b[i]); }
+ if (i) {
+ auto r = trim(b, e, beg, i);
+ if (r.first < r.second) { fn(&b[r.first], &b[r.second]); }
+ }
}
// NOTE: until the read size reaches `fixed_buffer_size`, use `fixed_buffer`
@@ -1172,7 +1611,17 @@ inline int close_socket(socket_t sock) {
#endif
}
-inline int select_read(socket_t sock, time_t sec, time_t usec) {
+template <typename T> inline ssize_t handle_EINTR(T fn) {
+ ssize_t res = false;
+ while (true) {
+ res = fn();
+ if (res < 0 && errno == EINTR) { continue; }
+ break;
+ }
+ return res;
+}
+
+inline ssize_t select_read(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@@ -1180,7 +1629,7 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
- return poll(&pfd_read, 1, timeout);
+ return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
#else
fd_set fds;
FD_ZERO(&fds);
@@ -1188,13 +1637,15 @@ inline int select_read(socket_t sock, time_t sec, time_t usec) {
timeval tv;
tv.tv_sec = static_cast<long>(sec);
- tv.tv_usec = static_cast<long>(usec);
+ tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
- return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
+ return handle_EINTR([&]() {
+ return select(static_cast<int>(sock + 1), &fds, nullptr, nullptr, &tv);
+ });
#endif
}
-inline int select_write(socket_t sock, time_t sec, time_t usec) {
+inline ssize_t select_write(socket_t sock, time_t sec, time_t usec) {
#ifdef CPPHTTPLIB_USE_POLL
struct pollfd pfd_read;
pfd_read.fd = sock;
@@ -1202,7 +1653,7 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
- return poll(&pfd_read, 1, timeout);
+ return handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
#else
fd_set fds;
FD_ZERO(&fds);
@@ -1210,9 +1661,11 @@ inline int select_write(socket_t sock, time_t sec, time_t usec) {
timeval tv;
tv.tv_sec = static_cast<long>(sec);
- tv.tv_usec = static_cast<long>(usec);
+ tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
- return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
+ return handle_EINTR([&]() {
+ return select(static_cast<int>(sock + 1), nullptr, &fds, nullptr, &tv);
+ });
#endif
}
@@ -1224,13 +1677,14 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
auto timeout = static_cast<int>(sec * 1000 + usec / 1000);
- if (poll(&pfd_read, 1, timeout) > 0 &&
- pfd_read.revents & (POLLIN | POLLOUT)) {
+ auto poll_res = handle_EINTR([&]() { return poll(&pfd_read, 1, timeout); });
+
+ if (poll_res > 0 && pfd_read.revents & (POLLIN | POLLOUT)) {
int error = 0;
socklen_t len = sizeof(error);
- return getsockopt(sock, SOL_SOCKET, SO_ERROR,
- reinterpret_cast<char *>(&error), &len) >= 0 &&
- !error;
+ auto res = getsockopt(sock, SOL_SOCKET, SO_ERROR,
+ reinterpret_cast<char *>(&error), &len);
+ return res >= 0 && !error;
}
return false;
#else
@@ -1243,10 +1697,13 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
timeval tv;
tv.tv_sec = static_cast<long>(sec);
- tv.tv_usec = static_cast<long>(usec);
+ tv.tv_usec = static_cast<decltype(tv.tv_usec)>(usec);
+
+ auto ret = handle_EINTR([&]() {
+ return select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv);
+ });
- if (select(static_cast<int>(sock + 1), &fdsr, &fdsw, &fdse, &tv) > 0 &&
- (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
+ if (ret > 0 && (FD_ISSET(sock, &fdsr) || FD_ISSET(sock, &fdsw))) {
int error = 0;
socklen_t len = sizeof(error);
return getsockopt(sock, SOL_SOCKET, SO_ERROR,
@@ -1259,40 +1716,45 @@ inline bool wait_until_socket_is_ready(socket_t sock, time_t sec, time_t usec) {
class SocketStream : public Stream {
public:
- SocketStream(socket_t sock, time_t read_timeout_sec,
- time_t read_timeout_usec);
+ SocketStream(socket_t sock, time_t read_timeout_sec, time_t read_timeout_usec,
+ time_t write_timeout_sec, time_t write_timeout_usec);
~SocketStream() override;
bool is_readable() const override;
bool is_writable() const override;
- int read(char *ptr, size_t size) override;
- int write(const char *ptr, size_t size) override;
- std::string get_remote_addr() const override;
+ ssize_t read(char *ptr, size_t size) override;
+ ssize_t write(const char *ptr, size_t size) override;
+ void get_remote_ip_and_port(std::string &ip, int &port) const override;
private:
socket_t sock_;
time_t read_timeout_sec_;
time_t read_timeout_usec_;
+ time_t write_timeout_sec_;
+ time_t write_timeout_usec_;
};
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
class SSLSocketStream : public Stream {
public:
SSLSocketStream(socket_t sock, SSL *ssl, time_t read_timeout_sec,
- time_t read_timeout_usec);
- virtual ~SSLSocketStream();
+ time_t read_timeout_usec, time_t write_timeout_sec,
+ time_t write_timeout_usec);
+ ~SSLSocketStream() override;
bool is_readable() const override;
bool is_writable() const override;
- int read(char *ptr, size_t size) override;
- int write(const char *ptr, size_t size) override;
- std::string get_remote_addr() const override;
+ ssize_t read(char *ptr, size_t size) override;
+ ssize_t write(const char *ptr, size_t size) override;
+ void get_remote_ip_and_port(std::string &ip, int &port) const override;
private:
socket_t sock_;
SSL *ssl_;
time_t read_timeout_sec_;
time_t read_timeout_usec_;
+ time_t write_timeout_sec_;
+ time_t write_timeout_usec_;
};
#endif
@@ -1303,58 +1765,76 @@ public:
bool is_readable() const override;
bool is_writable() const override;
- int read(char *ptr, size_t size) override;
- int write(const char *ptr, size_t size) override;
- std::string get_remote_addr() const override;
+ ssize_t read(char *ptr, size_t size) override;
+ ssize_t write(const char *ptr, size_t size) override;
+ void get_remote_ip_and_port(std::string &ip, int &port) const override;
const std::string &get_buffer() const;
private:
std::string buffer;
- int position = 0;
+ size_t position = 0;
};
+inline bool keep_alive(socket_t sock, time_t keep_alive_timeout_sec) {
+ using namespace std::chrono;
+ auto start = steady_clock::now();
+ while (true) {
+ auto val = select_read(sock, 0, 10000);
+ if (val < 0) {
+ return false;
+ } else if (val == 0) {
+ auto current = steady_clock::now();
+ auto duration = duration_cast<milliseconds>(current - start);
+ auto timeout = keep_alive_timeout_sec * 1000;
+ if (duration.count() > timeout) { return false; }
+ std::this_thread::sleep_for(std::chrono::milliseconds(1));
+ } else {
+ return true;
+ }
+ }
+}
+
template <typename T>
-inline bool process_socket(bool is_client_request, socket_t sock,
- size_t keep_alive_max_count, time_t read_timeout_sec,
- time_t read_timeout_usec, T callback) {
+inline bool
+process_server_socket_core(socket_t sock, size_t keep_alive_max_count,
+ time_t keep_alive_timeout_sec, T callback) {
assert(keep_alive_max_count > 0);
-
auto ret = false;
-
- if (keep_alive_max_count > 1) {
- auto count = keep_alive_max_count;
- while (count > 0 &&
- (is_client_request ||
- select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
- CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
- SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
- auto last_connection = count == 1;
- auto connection_close = false;
-
- ret = callback(strm, last_connection, connection_close);
- if (!ret || connection_close) { break; }
-
- count--;
- }
- } else { // keep_alive_max_count is 0 or 1
- SocketStream strm(sock, read_timeout_sec, read_timeout_usec);
- auto dummy_connection_close = false;
- ret = callback(strm, true, dummy_connection_close);
+ auto count = keep_alive_max_count;
+ while (count > 0 && keep_alive(sock, keep_alive_timeout_sec)) {
+ auto close_connection = count == 1;
+ auto connection_closed = false;
+ ret = callback(close_connection, connection_closed);
+ if (!ret || connection_closed) { break; }
+ count--;
}
-
return ret;
}
template <typename T>
-inline bool process_and_close_socket(bool is_client_request, socket_t sock,
- size_t keep_alive_max_count,
- time_t read_timeout_sec,
- time_t read_timeout_usec, T callback) {
- auto ret = process_socket(is_client_request, sock, keep_alive_max_count,
- read_timeout_sec, read_timeout_usec, callback);
- close_socket(sock);
- return ret;
+inline bool
+process_server_socket(socket_t sock, size_t keep_alive_max_count,
+ time_t keep_alive_timeout_sec, time_t read_timeout_sec,
+ time_t read_timeout_usec, time_t write_timeout_sec,
+ time_t write_timeout_usec, T callback) {
+ return process_server_socket_core(
+ sock, keep_alive_max_count, keep_alive_timeout_sec,
+ [&](bool close_connection, bool &connection_closed) {
+ SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
+ write_timeout_sec, write_timeout_usec);
+ return callback(strm, close_connection, connection_closed);
+ });
+}
+
+template <typename T>
+inline bool process_client_socket(socket_t sock, time_t read_timeout_sec,
+ time_t read_timeout_usec,
+ time_t write_timeout_sec,
+ time_t write_timeout_usec, T callback) {
+ SocketStream strm(sock, read_timeout_sec, read_timeout_usec,
+ write_timeout_sec, write_timeout_usec);
+ return callback(strm);
}
inline int shutdown_socket(socket_t sock) {
@@ -1365,18 +1845,10 @@ inline int shutdown_socket(socket_t sock) {
#endif
}
-template <typename Fn>
-socket_t create_socket(const char *host, int port, Fn fn,
- int socket_flags = 0) {
-#ifdef _WIN32
-#define SO_SYNCHRONOUS_NONALERT 0x20
-#define SO_OPENTYPE 0x7008
-
- int opt = SO_SYNCHRONOUS_NONALERT;
- setsockopt(INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, (char *)&opt,
- sizeof(opt));
-#endif
-
+template <typename BindOrConnect>
+socket_t create_socket(const char *host, int port, int socket_flags,
+ bool tcp_nodelay, SocketOptions socket_options,
+ BindOrConnect bind_or_connect) {
// Get address info
struct addrinfo hints;
struct addrinfo *result;
@@ -1390,6 +1862,9 @@ socket_t create_socket(const char *host, int port, Fn fn,
auto service = std::to_string(port);
if (getaddrinfo(host, service.c_str(), &hints, &result)) {
+#ifdef __linux__
+ res_init();
+#endif
return INVALID_SOCKET;
}
@@ -1424,17 +1899,22 @@ socket_t create_socket(const char *host, int port, Fn fn,
if (fcntl(sock, F_SETFD, FD_CLOEXEC) == -1) { continue; }
#endif
- // Make 'reuse address' option available
- int yes = 1;
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR, reinterpret_cast<char *>(&yes),
- sizeof(yes));
-#ifdef SO_REUSEPORT
- setsockopt(sock, SOL_SOCKET, SO_REUSEPORT, reinterpret_cast<char *>(&yes),
- sizeof(yes));
-#endif
+ if (tcp_nodelay) {
+ int yes = 1;
+ setsockopt(sock, IPPROTO_TCP, TCP_NODELAY, reinterpret_cast<char *>(&yes),
+ sizeof(yes));
+ }
+
+ if (socket_options) { socket_options(sock); }
+
+ if (rp->ai_family == AF_INET6) {
+ int no = 0;
+ setsockopt(sock, IPPROTO_IPV6, IPV6_V6ONLY, reinterpret_cast<char *>(&no),
+ sizeof(no));
+ }
// bind or connect
- if (fn(sock, *rp)) {
+ if (bind_or_connect(sock, *rp)) {
freeaddrinfo(result);
return sock;
}
@@ -1479,7 +1959,7 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
auto ret = false;
for (auto rp = result; rp; rp = rp->ai_next) {
const auto &ai = *rp;
- if (!::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
+ if (!::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
ret = true;
break;
}
@@ -1489,8 +1969,12 @@ inline bool bind_ip_address(socket_t sock, const char *host) {
return ret;
}
+#if !defined _WIN32 && !defined ANDROID
+#define USE_IF2IP
+#endif
+
+#ifdef USE_IF2IP
inline std::string if2ip(const std::string &ifn) {
-#ifndef _WIN32
struct ifaddrs *ifap;
getifaddrs(&ifap);
for (auto ifa = ifap; ifa; ifa = ifa->ifa_next) {
@@ -1506,51 +1990,83 @@ inline std::string if2ip(const std::string &ifn) {
}
}
freeifaddrs(ifap);
-#endif
return std::string();
}
+#endif
inline socket_t create_client_socket(const char *host, int port,
- time_t timeout_sec,
- const std::string &intf) {
- return create_socket(
- host, port, [&](socket_t sock, struct addrinfo &ai) -> bool {
+ bool tcp_nodelay,
+ SocketOptions socket_options,
+ time_t timeout_sec, time_t timeout_usec,
+ const std::string &intf, std::atomic<Error> &error) {
+ auto sock = create_socket(
+ host, port, 0, tcp_nodelay, std::move(socket_options),
+ [&](socket_t sock, struct addrinfo &ai) -> bool {
if (!intf.empty()) {
+#ifdef USE_IF2IP
auto ip = if2ip(intf);
if (ip.empty()) { ip = intf; }
- if (!bind_ip_address(sock, ip.c_str())) { return false; }
+ if (!bind_ip_address(sock, ip.c_str())) {
+ error = Error::BindIPAddress;
+ return false;
+ }
+#endif
}
set_nonblocking(sock, true);
- auto ret = ::connect(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen));
+ auto ret =
+ ::connect(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen));
+
if (ret < 0) {
if (is_connection_error() ||
- !wait_until_socket_is_ready(sock, timeout_sec, 0)) {
+ !wait_until_socket_is_ready(sock, timeout_sec, timeout_usec)) {
close_socket(sock);
+ error = Error::Connection;
return false;
}
}
set_nonblocking(sock, false);
+ error = Error::Success;
return true;
});
-}
-inline std::string get_remote_addr(socket_t sock) {
- struct sockaddr_storage addr;
- socklen_t len = sizeof(addr);
+ if (sock != INVALID_SOCKET) {
+ error = Error::Success;
+ } else {
+ if (error == Error::Success) { error = Error::Connection; }
+ }
- if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr), &len)) {
- std::array<char, NI_MAXHOST> ipstr{};
+ return sock;
+}
- if (!getnameinfo(reinterpret_cast<struct sockaddr *>(&addr), len,
- ipstr.data(), static_cast<unsigned int>(ipstr.size()), nullptr, 0, NI_NUMERICHOST)) {
- return ipstr.data();
- }
+inline void get_remote_ip_and_port(const struct sockaddr_storage &addr,
+ socklen_t addr_len, std::string &ip,
+ int &port) {
+ if (addr.ss_family == AF_INET) {
+ port = ntohs(reinterpret_cast<const struct sockaddr_in *>(&addr)->sin_port);
+ } else if (addr.ss_family == AF_INET6) {
+ port =
+ ntohs(reinterpret_cast<const struct sockaddr_in6 *>(&addr)->sin6_port);
}
- return std::string();
+ std::array<char, NI_MAXHOST> ipstr{};
+ if (!getnameinfo(reinterpret_cast<const struct sockaddr *>(&addr), addr_len,
+ ipstr.data(), static_cast<socklen_t>(ipstr.size()), nullptr,
+ 0, NI_NUMERICHOST)) {
+ ip = ipstr.data();
+ }
+}
+
+inline void get_remote_ip_and_port(socket_t sock, std::string &ip, int &port) {
+ struct sockaddr_storage addr;
+ socklen_t addr_len = sizeof(addr);
+
+ if (!getpeername(sock, reinterpret_cast<struct sockaddr *>(&addr),
+ &addr_len)) {
+ get_remote_ip_and_port(addr, addr_len, ip, port);
+ }
}
inline const char *
@@ -1596,121 +2112,336 @@ find_content_type(const std::string &path,
inline const char *status_message(int status) {
switch (status) {
case 100: return "Continue";
+ case 101: return "Switching Protocol";
+ case 102: return "Processing";
+ case 103: return "Early Hints";
case 200: return "OK";
+ case 201: return "Created";
case 202: return "Accepted";
+ case 203: return "Non-Authoritative Information";
case 204: return "No Content";
+ case 205: return "Reset Content";
case 206: return "Partial Content";
+ case 207: return "Multi-Status";
+ case 208: return "Already Reported";
+ case 226: return "IM Used";
+ case 300: return "Multiple Choice";
case 301: return "Moved Permanently";
case 302: return "Found";
case 303: return "See Other";
case 304: return "Not Modified";
+ case 305: return "Use Proxy";
+ case 306: return "unused";
+ case 307: return "Temporary Redirect";
+ case 308: return "Permanent Redirect";
case 400: return "Bad Request";
case 401: return "Unauthorized";
+ case 402: return "Payment Required";
case 403: return "Forbidden";
case 404: return "Not Found";
+ case 405: return "Method Not Allowed";
+ case 406: return "Not Acceptable";
+ case 407: return "Proxy Authentication Required";
+ case 408: return "Request Timeout";
+ case 409: return "Conflict";
+ case 410: return "Gone";
+ case 411: return "Length Required";
+ case 412: return "Precondition Failed";
case 413: return "Payload Too Large";
- case 414: return "Request-URI Too Long";
+ case 414: return "URI Too Long";
case 415: return "Unsupported Media Type";
case 416: return "Range Not Satisfiable";
case 417: return "Expectation Failed";
+ case 418: return "I'm a teapot";
+ case 421: return "Misdirected Request";
+ case 422: return "Unprocessable Entity";
+ case 423: return "Locked";
+ case 424: return "Failed Dependency";
+ case 425: return "Too Early";
+ case 426: return "Upgrade Required";
+ case 428: return "Precondition Required";
+ case 429: return "Too Many Requests";
+ case 431: return "Request Header Fields Too Large";
+ case 451: return "Unavailable For Legal Reasons";
+ case 501: return "Not Implemented";
+ case 502: return "Bad Gateway";
case 503: return "Service Unavailable";
+ case 504: return "Gateway Timeout";
+ case 505: return "HTTP Version Not Supported";
+ case 506: return "Variant Also Negotiates";
+ case 507: return "Insufficient Storage";
+ case 508: return "Loop Detected";
+ case 510: return "Not Extended";
+ case 511: return "Network Authentication Required";
default:
case 500: return "Internal Server Error";
}
}
-#ifdef CPPHTTPLIB_ZLIB_SUPPORT
-inline bool can_compress(const std::string &content_type) {
- return !content_type.find("text/") || content_type == "image/svg+xml" ||
+inline bool can_compress_content_type(const std::string &content_type) {
+ return (!content_type.find("text/") && content_type != "text/event-stream") ||
+ content_type == "image/svg+xml" ||
content_type == "application/javascript" ||
content_type == "application/json" ||
content_type == "application/xml" ||
content_type == "application/xhtml+xml";
}
-inline bool compress(std::string &content) {
- z_stream strm;
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
+enum class EncodingType { None = 0, Gzip, Brotli };
- auto ret = deflateInit2(&strm, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
- Z_DEFAULT_STRATEGY);
- if (ret != Z_OK) { return false; }
+inline EncodingType encoding_type(const Request &req, const Response &res) {
+ auto ret =
+ detail::can_compress_content_type(res.get_header_value("Content-Type"));
+ if (!ret) { return EncodingType::None; }
- strm.avail_in = content.size();
- strm.next_in =
- const_cast<Bytef *>(reinterpret_cast<const Bytef *>(content.data()));
+ const auto &s = req.get_header_value("Accept-Encoding");
+ (void)(s);
- std::string compressed;
+#ifdef CPPHTTPLIB_BROTLI_SUPPORT
+ // TODO: 'Accept-Encoding' has br, not br;q=0
+ ret = s.find("br") != std::string::npos;
+ if (ret) { return EncodingType::Brotli; }
+#endif
- std::array<char, 16384> buff{};
- do {
- strm.avail_out = buff.size();
- strm.next_out = reinterpret_cast<Bytef *>(buff.data());
- ret = deflate(&strm, Z_FINISH);
- assert(ret != Z_STREAM_ERROR);
- compressed.append(buff.data(), buff.size() - strm.avail_out);
- } while (strm.avail_out == 0);
+#ifdef CPPHTTPLIB_ZLIB_SUPPORT
+ // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
+ ret = s.find("gzip") != std::string::npos;
+ if (ret) { return EncodingType::Gzip; }
+#endif
- assert(ret == Z_STREAM_END);
- assert(strm.avail_in == 0);
+ return EncodingType::None;
+}
- content.swap(compressed);
+class compressor {
+public:
+ virtual ~compressor(){};
- deflateEnd(&strm);
- return true;
-}
+ typedef std::function<bool(const char *data, size_t data_len)> Callback;
+ virtual bool compress(const char *data, size_t data_length, bool last,
+ Callback callback) = 0;
+};
class decompressor {
public:
- decompressor() {
- strm.zalloc = Z_NULL;
- strm.zfree = Z_NULL;
- strm.opaque = Z_NULL;
+ virtual ~decompressor() {}
+
+ virtual bool is_valid() const = 0;
+
+ typedef std::function<bool(const char *data, size_t data_len)> Callback;
+ virtual bool decompress(const char *data, size_t data_length,
+ Callback callback) = 0;
+};
+
+class nocompressor : public compressor {
+public:
+ ~nocompressor(){};
+
+ bool compress(const char *data, size_t data_length, bool /*last*/,
+ Callback callback) override {
+ if (!data_length) { return true; }
+ return callback(data, data_length);
+ }
+};
+
+#ifdef CPPHTTPLIB_ZLIB_SUPPORT
+class gzip_compressor : public compressor {
+public:
+ gzip_compressor() {
+ std::memset(&strm_, 0, sizeof(strm_));
+ strm_.zalloc = Z_NULL;
+ strm_.zfree = Z_NULL;
+ strm_.opaque = Z_NULL;
+
+ is_valid_ = deflateInit2(&strm_, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 31, 8,
+ Z_DEFAULT_STRATEGY) == Z_OK;
+ }
+
+ ~gzip_compressor() { deflateEnd(&strm_); }
+
+ bool compress(const char *data, size_t data_length, bool last,
+ Callback callback) override {
+ assert(is_valid_);
+
+ auto flush = last ? Z_FINISH : Z_NO_FLUSH;
+
+ strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
+ strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
+
+ int ret = Z_OK;
+
+ std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
+ do {
+ strm_.avail_out = buff.size();
+ strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
+
+ ret = deflate(&strm_, flush);
+ assert(ret != Z_STREAM_ERROR);
+
+ if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
+ return false;
+ }
+ } while (strm_.avail_out == 0);
+
+ assert((last && ret == Z_STREAM_END) || (!last && ret == Z_OK));
+ assert(strm_.avail_in == 0);
+ return true;
+ }
+
+private:
+ bool is_valid_ = false;
+ z_stream strm_;
+};
+
+class gzip_decompressor : public decompressor {
+public:
+ gzip_decompressor() {
+ std::memset(&strm_, 0, sizeof(strm_));
+ strm_.zalloc = Z_NULL;
+ strm_.zfree = Z_NULL;
+ strm_.opaque = Z_NULL;
// 15 is the value of wbits, which should be at the maximum possible value
- // to ensure that any gzip stream can be decoded. The offset of 16 specifies
- // that the stream to decompress will be formatted with a gzip wrapper.
- is_valid_ = inflateInit2(&strm, 16 + 15) == Z_OK;
+ // to ensure that any gzip stream can be decoded. The offset of 32 specifies
+ // that the stream type should be automatically detected either gzip or
+ // deflate.
+ is_valid_ = inflateInit2(&strm_, 32 + 15) == Z_OK;
}
- ~decompressor() { inflateEnd(&strm); }
+ ~gzip_decompressor() { inflateEnd(&strm_); }
- bool is_valid() const { return is_valid_; }
+ bool is_valid() const override { return is_valid_; }
+
+ bool decompress(const char *data, size_t data_length,
+ Callback callback) override {
+ assert(is_valid_);
- template <typename T>
- bool decompress(const char *data, size_t data_length, T callback) {
int ret = Z_OK;
- strm.avail_in = data_length;
- strm.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
+ strm_.avail_in = static_cast<decltype(strm_.avail_in)>(data_length);
+ strm_.next_in = const_cast<Bytef *>(reinterpret_cast<const Bytef *>(data));
- std::array<char, 16384> buff{};
- do {
- strm.avail_out = buff.size();
- strm.next_out = reinterpret_cast<Bytef *>(buff.data());
+ std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
+ while (strm_.avail_in > 0) {
+ strm_.avail_out = buff.size();
+ strm_.next_out = reinterpret_cast<Bytef *>(buff.data());
- ret = inflate(&strm, Z_NO_FLUSH);
+ ret = inflate(&strm_, Z_NO_FLUSH);
assert(ret != Z_STREAM_ERROR);
switch (ret) {
case Z_NEED_DICT:
case Z_DATA_ERROR:
- case Z_MEM_ERROR: inflateEnd(&strm); return false;
+ case Z_MEM_ERROR: inflateEnd(&strm_); return false;
}
- if (!callback(buff.data(), buff.size() - strm.avail_out)) {
+ if (!callback(buff.data(), buff.size() - strm_.avail_out)) {
return false;
}
- } while (strm.avail_out == 0);
+ }
return ret == Z_OK || ret == Z_STREAM_END;
}
private:
- bool is_valid_;
- z_stream strm;
+ bool is_valid_ = false;
+ z_stream strm_;
+};
+#endif
+
+#ifdef CPPHTTPLIB_BROTLI_SUPPORT
+class brotli_compressor : public compressor {
+public:
+ brotli_compressor() {
+ state_ = BrotliEncoderCreateInstance(nullptr, nullptr, nullptr);
+ }
+
+ ~brotli_compressor() { BrotliEncoderDestroyInstance(state_); }
+
+ bool compress(const char *data, size_t data_length, bool last,
+ Callback callback) override {
+ std::array<uint8_t, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
+
+ auto operation = last ? BROTLI_OPERATION_FINISH : BROTLI_OPERATION_PROCESS;
+ auto available_in = data_length;
+ auto next_in = reinterpret_cast<const uint8_t *>(data);
+
+ for (;;) {
+ if (last) {
+ if (BrotliEncoderIsFinished(state_)) { break; }
+ } else {
+ if (!available_in) { break; }
+ }
+
+ auto available_out = buff.size();
+ auto next_out = buff.data();
+
+ if (!BrotliEncoderCompressStream(state_, operation, &available_in,
+ &next_in, &available_out, &next_out,
+ nullptr)) {
+ return false;
+ }
+
+ auto output_bytes = buff.size() - available_out;
+ if (output_bytes) {
+ callback(reinterpret_cast<const char *>(buff.data()), output_bytes);
+ }
+ }
+
+ return true;
+ }
+
+private:
+ BrotliEncoderState *state_ = nullptr;
+};
+
+class brotli_decompressor : public decompressor {
+public:
+ brotli_decompressor() {
+ decoder_s = BrotliDecoderCreateInstance(0, 0, 0);
+ decoder_r = decoder_s ? BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT
+ : BROTLI_DECODER_RESULT_ERROR;
+ }
+
+ ~brotli_decompressor() {
+ if (decoder_s) { BrotliDecoderDestroyInstance(decoder_s); }
+ }
+
+ bool is_valid() const override { return decoder_s; }
+
+ bool decompress(const char *data, size_t data_length,
+ Callback callback) override {
+ if (decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
+ decoder_r == BROTLI_DECODER_RESULT_ERROR) {
+ return 0;
+ }
+
+ const uint8_t *next_in = (const uint8_t *)data;
+ size_t avail_in = data_length;
+ size_t total_out;
+
+ decoder_r = BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT;
+
+ std::array<char, CPPHTTPLIB_COMPRESSION_BUFSIZ> buff{};
+ while (decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_OUTPUT) {
+ char *next_out = buff.data();
+ size_t avail_out = buff.size();
+
+ decoder_r = BrotliDecoderDecompressStream(
+ decoder_s, &avail_in, &next_in, &avail_out,
+ reinterpret_cast<uint8_t **>(&next_out), &total_out);
+
+ if (decoder_r == BROTLI_DECODER_RESULT_ERROR) { return false; }
+
+ if (!callback(buff.data(), buff.size() - avail_out)) { return false; }
+ }
+
+ return decoder_r == BROTLI_DECODER_RESULT_SUCCESS ||
+ decoder_r == BROTLI_DECODER_RESULT_NEEDS_MORE_INPUT;
+ }
+
+private:
+ BrotliDecoderResult decoder_r;
+ BrotliDecoderState *decoder_s = nullptr;
};
#endif
@@ -1720,21 +2451,60 @@ inline bool has_header(const Headers &headers, const char *key) {
inline const char *get_header_value(const Headers &headers, const char *key,
size_t id = 0, const char *def = nullptr) {
- auto it = headers.find(key);
- std::advance(it, id);
- if (it != headers.end()) { return it->second.c_str(); }
+ auto rng = headers.equal_range(key);
+ auto it = rng.first;
+ std::advance(it, static_cast<ssize_t>(id));
+ if (it != rng.second) { return it->second.c_str(); }
return def;
}
-inline uint64_t get_header_value_uint64(const Headers &headers, const char *key,
- int def = 0) {
- auto it = headers.find(key);
- if (it != headers.end()) {
+template <typename T>
+inline T get_header_value(const Headers & /*headers*/, const char * /*key*/,
+ size_t /*id*/ = 0, uint64_t /*def*/ = 0) {}
+
+template <>
+inline uint64_t get_header_value<uint64_t>(const Headers &headers,
+ const char *key, size_t id,
+ uint64_t def) {
+ auto rng = headers.equal_range(key);
+ auto it = rng.first;
+ std::advance(it, static_cast<ssize_t>(id));
+ if (it != rng.second) {
return std::strtoull(it->second.data(), nullptr, 10);
}
return def;
}
+template <typename T>
+inline bool parse_header(const char *beg, const char *end, T fn) {
+ // Skip trailing spaces and tabs.
+ while (beg < end && is_space_or_tab(end[-1])) {
+ end--;
+ }
+
+ auto p = beg;
+ while (p < end && *p != ':') {
+ p++;
+ }
+
+ if (p == end) { return false; }
+
+ auto key_end = p;
+
+ if (*p++ != ':') { return false; }
+
+ while (p < end && is_space_or_tab(*p)) {
+ p++;
+ }
+
+ if (p < end) {
+ fn(std::string(beg, key_end), decode_url(std::string(p, end), false));
+ return true;
+ }
+
+ return false;
+}
+
inline bool read_headers(Stream &strm, Headers &headers) {
const auto bufsiz = 2048;
char buf[bufsiz];
@@ -1751,42 +2521,31 @@ inline bool read_headers(Stream &strm, Headers &headers) {
continue; // Skip invalid line.
}
- // Skip trailing spaces and tabs.
+ // Exclude CRLF
auto end = line_reader.ptr() + line_reader.size() - 2;
- while (line_reader.ptr() < end && (end[-1] == ' ' || end[-1] == '\t')) {
- end--;
- }
- // Horizontal tab and ' ' are considered whitespace and are ignored when on
- // the left or right side of the header value:
- // - https://stackoverflow.com/questions/50179659/
- // - https://www.w3.org/Protocols/rfc2616/rfc2616-sec4.html
- static const std::regex re(R"((.+?):[\t ]*(.+))");
-
- std::cmatch m;
- if (std::regex_match(line_reader.ptr(), end, m, re)) {
- auto key = std::string(m[1]);
- auto val = std::string(m[2]);
- headers.emplace(key, val);
- }
+ parse_header(line_reader.ptr(), end,
+ [&](std::string &&key, std::string &&val) {
+ headers.emplace(std::move(key), std::move(val));
+ });
}
return true;
}
inline bool read_content_with_length(Stream &strm, uint64_t len,
- Progress progress, ContentReceiver out) {
+ Progress progress,
+ ContentReceiverWithProgress out) {
char buf[CPPHTTPLIB_RECV_BUFSIZ];
uint64_t r = 0;
while (r < len) {
auto read_len = static_cast<size_t>(len - r);
- auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
+ auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return false; }
- if (!out(buf, n)) { return false; }
-
- r += n;
+ if (!out(buf, static_cast<size_t>(n), r, len)) { return false; }
+ r += static_cast<uint64_t>(n);
if (progress) {
if (!progress(r, len)) { return false; }
@@ -1801,14 +2560,16 @@ inline void skip_content_with_length(Stream &strm, uint64_t len) {
uint64_t r = 0;
while (r < len) {
auto read_len = static_cast<size_t>(len - r);
- auto n = strm.read(buf, std::min(read_len, CPPHTTPLIB_RECV_BUFSIZ));
+ auto n = strm.read(buf, (std::min)(read_len, CPPHTTPLIB_RECV_BUFSIZ));
if (n <= 0) { return; }
- r += n;
+ r += static_cast<uint64_t>(n);
}
}
-inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
+inline bool read_content_without_length(Stream &strm,
+ ContentReceiverWithProgress out) {
char buf[CPPHTTPLIB_RECV_BUFSIZ];
+ uint64_t r = 0;
for (;;) {
auto n = strm.read(buf, CPPHTTPLIB_RECV_BUFSIZ);
if (n < 0) {
@@ -1816,13 +2577,16 @@ inline bool read_content_without_length(Stream &strm, ContentReceiver out) {
} else if (n == 0) {
return true;
}
- if (!out(buf, n)) { return false; }
+
+ if (!out(buf, static_cast<size_t>(n), r, 0)) { return false; }
+ r += static_cast<uint64_t>(n);
}
return true;
}
-inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
+inline bool read_content_chunked(Stream &strm,
+ ContentReceiverWithProgress out) {
const auto bufsiz = 16;
char buf[bufsiz];
@@ -1830,9 +2594,17 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
if (!line_reader.getline()) { return false; }
- auto chunk_len = std::stoi(line_reader.ptr(), 0, 16);
+ unsigned long chunk_len;
+ while (true) {
+ char *end_ptr;
+
+ chunk_len = std::strtoul(line_reader.ptr(), &end_ptr, 16);
+
+ if (end_ptr == line_reader.ptr()) { return false; }
+ if (chunk_len == ULONG_MAX) { return false; }
+
+ if (chunk_len == 0) { break; }
- while (chunk_len > 0) {
if (!read_content_with_length(strm, chunk_len, nullptr, out)) {
return false;
}
@@ -1842,8 +2614,6 @@ inline bool read_content_chunked(Stream &strm, ContentReceiver out) {
if (strcmp(line_reader.ptr(), "\r\n")) { break; }
if (!line_reader.getline()) { return false; }
-
- chunk_len = std::stoi(line_reader.ptr(), 0, 16);
}
if (chunk_len == 0) {
@@ -1860,62 +2630,91 @@ inline bool is_chunked_transfer_encoding(const Headers &headers) {
"chunked");
}
-template <typename T>
-bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
- Progress progress, ContentReceiver receiver) {
-
- ContentReceiver out = [&](const char *buf, size_t n) {
- return receiver(buf, n);
- };
+template <typename T, typename U>
+bool prepare_content_receiver(T &x, int &status,
+ ContentReceiverWithProgress receiver,
+ bool decompress, U callback) {
+ if (decompress) {
+ std::string encoding = x.get_header_value("Content-Encoding");
+ std::unique_ptr<decompressor> decompressor;
+ if (encoding.find("gzip") != std::string::npos ||
+ encoding.find("deflate") != std::string::npos) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
- decompressor decompressor;
-
- if (!decompressor.is_valid()) {
- status = 500;
- return false;
- }
-
- if (x.get_header_value("Content-Encoding") == "gzip") {
- out = [&](const char *buf, size_t n) {
- return decompressor.decompress(
- buf, n, [&](const char *buf, size_t n) { return receiver(buf, n); });
- };
- }
+ decompressor = detail::make_unique<gzip_decompressor>();
#else
- if (x.get_header_value("Content-Encoding") == "gzip") {
- status = 415;
- return false;
- }
+ status = 415;
+ return false;
#endif
+ } else if (encoding.find("br") != std::string::npos) {
+#ifdef CPPHTTPLIB_BROTLI_SUPPORT
+ decompressor = detail::make_unique<brotli_decompressor>();
+#else
+ status = 415;
+ return false;
+#endif
+ }
- auto ret = true;
- auto exceed_payload_max_length = false;
-
- if (is_chunked_transfer_encoding(x.headers)) {
- ret = read_content_chunked(strm, out);
- } else if (!has_header(x.headers, "Content-Length")) {
- ret = read_content_without_length(strm, out);
- } else {
- auto len = get_header_value_uint64(x.headers, "Content-Length", 0);
- if (len > payload_max_length) {
- exceed_payload_max_length = true;
- skip_content_with_length(strm, len);
- ret = false;
- } else if (len > 0) {
- ret = read_content_with_length(strm, len, progress, out);
+ if (decompressor) {
+ if (decompressor->is_valid()) {
+ ContentReceiverWithProgress out = [&](const char *buf, size_t n,
+ uint64_t off, uint64_t len) {
+ return decompressor->decompress(buf, n,
+ [&](const char *buf, size_t n) {
+ return receiver(buf, n, off, len);
+ });
+ };
+ return callback(std::move(out));
+ } else {
+ status = 500;
+ return false;
+ }
}
}
- if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
+ ContentReceiverWithProgress out = [&](const char *buf, size_t n, uint64_t off,
+ uint64_t len) {
+ return receiver(buf, n, off, len);
+ };
+ return callback(std::move(out));
+}
- return ret;
+template <typename T>
+bool read_content(Stream &strm, T &x, size_t payload_max_length, int &status,
+ Progress progress, ContentReceiverWithProgress receiver,
+ bool decompress) {
+ return prepare_content_receiver(
+ x, status, std::move(receiver), decompress,
+ [&](const ContentReceiverWithProgress &out) {
+ auto ret = true;
+ auto exceed_payload_max_length = false;
+
+ if (is_chunked_transfer_encoding(x.headers)) {
+ ret = read_content_chunked(strm, out);
+ } else if (!has_header(x.headers, "Content-Length")) {
+ ret = read_content_without_length(strm, out);
+ } else {
+ auto len = get_header_value<uint64_t>(x.headers, "Content-Length");
+ if (len > payload_max_length) {
+ exceed_payload_max_length = true;
+ skip_content_with_length(strm, len);
+ ret = false;
+ } else if (len > 0) {
+ ret = read_content_with_length(strm, len, std::move(progress), out);
+ }
+ }
+
+ if (!ret) { status = exceed_payload_max_length ? 413 : 400; }
+ return ret;
+ });
}
template <typename T>
-inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
- auto write_len = 0;
+inline ssize_t write_headers(Stream &strm, const T &info,
+ const Headers &headers) {
+ ssize_t write_len = 0;
for (const auto &x : info.headers) {
+ if (x.first == "EXCEPTION_WHAT") { continue; }
auto len =
strm.write_format("%s: %s\r\n", x.first.c_str(), x.second.c_str());
if (len < 0) { return len; }
@@ -1933,57 +2732,150 @@ inline int write_headers(Stream &strm, const T &info, const Headers &headers) {
return write_len;
}
+inline bool write_data(Stream &strm, const char *d, size_t l) {
+ size_t offset = 0;
+ while (offset < l) {
+ auto length = strm.write(d + offset, l - offset);
+ if (length < 0) { return false; }
+ offset += static_cast<size_t>(length);
+ }
+ return true;
+}
+
+template <typename T>
inline ssize_t write_content(Stream &strm, ContentProvider content_provider,
- size_t offset, size_t length) {
+ size_t offset, size_t length, T is_shutting_down) {
size_t begin_offset = offset;
size_t end_offset = offset + length;
- while (offset < end_offset) {
- ssize_t written_length = 0;
+ auto ok = true;
+ DataSink data_sink;
- DataSink data_sink;
- data_sink.write = [&](const char *d, size_t l) {
+ data_sink.write = [&](const char *d, size_t l) {
+ if (ok) {
offset += l;
- written_length = strm.write(d, l);
- };
- data_sink.done = [&](void) { written_length = -1; };
- data_sink.is_writable = [&](void) { return strm.is_writable(); };
+ if (!write_data(strm, d, l)) { ok = false; }
+ }
+ };
+
+ data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
- content_provider(offset, end_offset - offset, data_sink);
- if (written_length < 0) { return written_length; }
+ while (offset < end_offset && !is_shutting_down()) {
+ if (!content_provider(offset, end_offset - offset, data_sink)) {
+ return -1;
+ }
+ if (!ok) { return -1; }
}
+
return static_cast<ssize_t>(offset - begin_offset);
}
template <typename T>
+inline ssize_t write_content_without_length(Stream &strm,
+ ContentProvider content_provider,
+ T is_shutting_down) {
+ size_t offset = 0;
+ auto data_available = true;
+ auto ok = true;
+ DataSink data_sink;
+
+ data_sink.write = [&](const char *d, size_t l) {
+ if (ok) {
+ offset += l;
+ if (!write_data(strm, d, l)) { ok = false; }
+ }
+ };
+
+ data_sink.done = [&](void) { data_available = false; };
+
+ data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
+
+ while (data_available && !is_shutting_down()) {
+ if (!content_provider(offset, 0, data_sink)) { return -1; }
+ if (!ok) { return -1; }
+ }
+
+ return static_cast<ssize_t>(offset);
+}
+
+template <typename T, typename U>
inline ssize_t write_content_chunked(Stream &strm,
ContentProvider content_provider,
- T is_shutting_down) {
+ T is_shutting_down, U &compressor) {
size_t offset = 0;
auto data_available = true;
ssize_t total_written_length = 0;
- while (data_available && !is_shutting_down()) {
- ssize_t written_length = 0;
+ auto ok = true;
+ DataSink data_sink;
+
+ data_sink.write = [&](const char *d, size_t l) {
+ if (!ok) { return; }
+
+ data_available = l > 0;
+ offset += l;
+
+ std::string payload;
+ if (!compressor.compress(d, l, false,
+ [&](const char *data, size_t data_len) {
+ payload.append(data, data_len);
+ return true;
+ })) {
+ ok = false;
+ return;
+ }
- DataSink data_sink;
- data_sink.write = [&](const char *d, size_t l) {
- data_available = l > 0;
- offset += l;
+ if (!payload.empty()) {
+ // Emit chunked response header and footer for each chunk
+ auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
+ if (write_data(strm, chunk.data(), chunk.size())) {
+ total_written_length += chunk.size();
+ } else {
+ ok = false;
+ return;
+ }
+ }
+ };
+
+ data_sink.done = [&](void) {
+ if (!ok) { return; }
+ data_available = false;
+
+ std::string payload;
+ if (!compressor.compress(nullptr, 0, true,
+ [&](const char *data, size_t data_len) {
+ payload.append(data, data_len);
+ return true;
+ })) {
+ ok = false;
+ return;
+ }
+
+ if (!payload.empty()) {
// Emit chunked response header and footer for each chunk
- auto chunk = from_i_to_hex(l) + "\r\n" + std::string(d, l) + "\r\n";
- written_length = strm.write(chunk);
- };
- data_sink.done = [&](void) {
- data_available = false;
- written_length = strm.write("0\r\n\r\n");
- };
- data_sink.is_writable = [&](void) { return strm.is_writable(); };
+ auto chunk = from_i_to_hex(payload.size()) + "\r\n" + payload + "\r\n";
+ if (write_data(strm, chunk.data(), chunk.size())) {
+ total_written_length += chunk.size();
+ } else {
+ ok = false;
+ return;
+ }
+ }
+
+ static const std::string done_marker("0\r\n\r\n");
+ if (write_data(strm, done_marker.data(), done_marker.size())) {
+ total_written_length += done_marker.size();
+ } else {
+ ok = false;
+ }
+ };
- content_provider(offset, 0, data_sink);
+ data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
- if (written_length < 0) { return written_length; }
- total_written_length += written_length;
+ while (data_available && !is_shutting_down()) {
+ if (!content_provider(offset, 0, data_sink)) { return -1; }
+ if (!ok) { return -1; }
}
+
return total_written_length;
}
@@ -1994,6 +2886,12 @@ inline bool redirect(T &cli, const Request &req, Response &res,
new_req.path = path;
new_req.redirect_count -= 1;
+ if (res.status == 303 && (req.method != "GET" && req.method != "HEAD")) {
+ new_req.method = "GET";
+ new_req.body.clear();
+ new_req.headers.clear();
+ }
+
Response new_res;
auto ret = cli.send(new_req, new_res);
@@ -2001,75 +2899,20 @@ inline bool redirect(T &cli, const Request &req, Response &res,
return ret;
}
-inline std::string encode_url(const std::string &s) {
- std::string result;
-
- for (auto i = 0; s[i]; i++) {
- switch (s[i]) {
- case ' ': result += "%20"; break;
- case '+': result += "%2B"; break;
- case '\r': result += "%0D"; break;
- case '\n': result += "%0A"; break;
- case '\'': result += "%27"; break;
- case ',': result += "%2C"; break;
- // case ':': result += "%3A"; break; // ok? probably...
- case ';': result += "%3B"; break;
- default:
- auto c = static_cast<uint8_t>(s[i]);
- if (c >= 0x80) {
- result += '%';
- char hex[4];
- size_t len = snprintf(hex, sizeof(hex) - 1, "%02X", c);
- assert(len == 2);
- result.append(hex, len);
- } else {
- result += s[i];
- }
- break;
- }
- }
-
- return result;
-}
-
-inline std::string decode_url(const std::string &s) {
- std::string result;
+inline std::string params_to_query_str(const Params &params) {
+ std::string query;
- for (size_t i = 0; i < s.size(); i++) {
- if (s[i] == '%' && i + 1 < s.size()) {
- if (s[i + 1] == 'u') {
- int val = 0;
- if (from_hex_to_i(s, i + 2, 4, val)) {
- // 4 digits Unicode codes
- char buff[4];
- size_t len = to_utf8(val, buff);
- if (len > 0) { result.append(buff, len); }
- i += 5; // 'u0000'
- } else {
- result += s[i];
- }
- } else {
- int val = 0;
- if (from_hex_to_i(s, i + 1, 2, val)) {
- // 2 digits hex codes
- result += static_cast<char>(val);
- i += 2; // '00'
- } else {
- result += s[i];
- }
- }
- } else if (s[i] == '+') {
- result += ' ';
- } else {
- result += s[i];
- }
+ for (auto it = params.begin(); it != params.end(); ++it) {
+ if (it != params.begin()) { query += "&"; }
+ query += it->first;
+ query += "=";
+ query += encode_url(it->second);
}
-
- return result;
+ return query;
}
inline void parse_query_text(const std::string &s, Params &params) {
- split(&s[0], &s[s.size()], '&', [&](const char *b, const char *e) {
+ split(s.data(), s.data() + s.size(), '&', [&](const char *b, const char *e) {
std::string key;
std::string val;
split(b, e, '=', [&](const char *b2, const char *e2) {
@@ -2079,7 +2922,10 @@ inline void parse_query_text(const std::string &s, Params &params) {
val.assign(b2, e2);
}
});
- params.emplace(key, decode_url(val));
+
+ if (!key.empty()) {
+ params.emplace(decode_url(key, true), decode_url(val, true));
+ }
});
}
@@ -2087,17 +2933,20 @@ inline bool parse_multipart_boundary(const std::string &content_type,
std::string &boundary) {
auto pos = content_type.find("boundary=");
if (pos == std::string::npos) { return false; }
-
boundary = content_type.substr(pos + 9);
- return true;
+ if (boundary.length() >= 2 && boundary.front() == '"' &&
+ boundary.back() == '"') {
+ boundary = boundary.substr(1, boundary.size() - 2);
+ }
+ return !boundary.empty();
}
-inline bool parse_range_header(const std::string &s, Ranges &ranges) {
+inline bool parse_range_header(const std::string &s, Ranges &ranges) try {
static auto re_first_range = std::regex(R"(bytes=(\d*-\d*(?:,\s*\d*-\d*)*))");
std::smatch m;
if (std::regex_match(s, m, re_first_range)) {
- auto pos = m.position(1);
- auto len = m.length(1);
+ auto pos = static_cast<size_t>(m.position(1));
+ auto len = static_cast<size_t>(m.length(1));
bool all_valid_ranges = true;
split(&s[pos], &s[pos + len], ',', [&](const char *b, const char *e) {
if (!all_valid_ranges) return;
@@ -2124,25 +2973,26 @@ inline bool parse_range_header(const std::string &s, Ranges &ranges) {
return all_valid_ranges;
}
return false;
-}
+} catch (...) { return false; }
class MultipartFormDataParser {
public:
- MultipartFormDataParser() {}
+ MultipartFormDataParser() = default;
- void set_boundary(const std::string &boundary) { boundary_ = boundary; }
+ void set_boundary(std::string &&boundary) { boundary_ = boundary; }
bool is_valid() const { return is_valid_; }
template <typename T, typename U>
- bool parse(const char *buf, size_t n, T content_callback, U header_callback) {
- static const std::regex re_content_type(R"(^Content-Type:\s*(.*?)\s*$)",
- std::regex_constants::icase);
+ bool parse(const char *buf, size_t n, const T &content_callback,
+ const U &header_callback) {
static const std::regex re_content_disposition(
"^Content-Disposition:\\s*form-data;\\s*name=\"(.*?)\"(?:;\\s*filename="
"\"(.*?)\")?\\s*$",
std::regex_constants::icase);
+ static const std::string dash_ = "--";
+ static const std::string crlf_ = "\r\n";
buf_.append(buf, n); // TODO: performance improvement
@@ -2152,10 +3002,7 @@ public:
auto pattern = dash_ + boundary_ + crlf_;
if (pattern.size() > buf_.size()) { return true; }
auto pos = buf_.find(pattern);
- if (pos != 0) {
- is_done_ = true;
- return false;
- }
+ if (pos != 0) { return false; }
buf_.erase(0, pattern.size());
off_ += pattern.size();
state_ = 1;
@@ -2173,7 +3020,6 @@ public:
if (pos == 0) {
if (!header_callback(file_)) {
is_valid_ = false;
- is_done_ = false;
return false;
}
buf_.erase(0, crlf_.size());
@@ -2182,12 +3028,13 @@ public:
break;
}
- auto header = buf_.substr(0, pos);
- {
+ static const std::string header_name = "content-type:";
+ const auto header = buf_.substr(0, pos);
+ if (start_with(header, header_name)) {
+ file_.content_type = trim_copy(header.substr(header_name.size()));
+ } else {
std::smatch m;
- if (std::regex_match(header, m, re_content_type)) {
- file_.content_type = m[1];
- } else if (std::regex_match(header, m, re_content_disposition)) {
+ if (std::regex_match(header, m, re_content_disposition)) {
file_.name = m[1];
file_.filename = m[2];
}
@@ -2197,6 +3044,7 @@ public:
off_ += pos + crlf_.size();
pos = buf_.find(crlf_);
}
+ if (state_ != 3) { return true; }
break;
}
case 3: { // Body
@@ -2205,10 +3053,17 @@ public:
if (pattern.size() > buf_.size()) { return true; }
auto pos = buf_.find(pattern);
- if (pos == std::string::npos) { pos = buf_.size(); }
+ if (pos == std::string::npos) {
+ pos = buf_.size();
+ while (pos > 0) {
+ auto c = buf_[pos - 1];
+ if (c != '\r' && c != '\n' && c != '-') { break; }
+ pos--;
+ }
+ }
+
if (!content_callback(buf_.data(), pos)) {
is_valid_ = false;
- is_done_ = false;
return false;
}
@@ -2224,7 +3079,6 @@ public:
if (pos != std::string::npos) {
if (!content_callback(buf_.data(), pos)) {
is_valid_ = false;
- is_done_ = false;
return false;
}
@@ -2234,7 +3088,6 @@ public:
} else {
if (!content_callback(buf_.data(), pattern.size())) {
is_valid_ = false;
- is_done_ = false;
return false;
}
@@ -2246,20 +3099,19 @@ public:
}
case 4: { // Boundary
if (crlf_.size() > buf_.size()) { return true; }
- if (buf_.find(crlf_) == 0) {
+ if (buf_.compare(0, crlf_.size(), crlf_) == 0) {
buf_.erase(0, crlf_.size());
off_ += crlf_.size();
state_ = 1;
} else {
auto pattern = dash_ + crlf_;
if (pattern.size() > buf_.size()) { return true; }
- if (buf_.find(pattern) == 0) {
+ if (buf_.compare(0, pattern.size(), pattern) == 0) {
buf_.erase(0, pattern.size());
off_ += pattern.size();
is_valid_ = true;
state_ = 5;
} else {
- is_done_ = true;
return true;
}
}
@@ -2282,14 +3134,11 @@ private:
file_.content_type.clear();
}
- const std::string dash_ = "--";
- const std::string crlf_ = "\r\n";
std::string boundary_;
std::string buf_;
size_t state_ = 0;
- size_t is_valid_ = false;
- size_t is_done_ = false;
+ bool is_valid_ = false;
size_t off_ = 0;
MultipartFormData file_;
};
@@ -2308,8 +3157,13 @@ inline std::string make_multipart_data_boundary() {
static const char data[] =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
+ // std::random_device might actually be deterministic on some
+ // platforms, but due to lack of support in the c++ standard library,
+ // doing better requires either some ugly hacks or breaking portability.
std::random_device seed_gen;
- std::mt19937 engine(seed_gen());
+ // Request 128 bits of entropy for initialization
+ std::seed_seq seed_sequence{seed_gen(), seed_gen(), seed_gen(), seed_gen()};
+ std::mt19937 engine(seed_sequence);
std::string result = "--cpp-httplib-multipart-data-";
@@ -2329,12 +3183,14 @@ get_range_offset_and_length(const Request &req, size_t content_length,
return std::make_pair(0, content_length);
}
+ auto slen = static_cast<ssize_t>(content_length);
+
if (r.first == -1) {
- r.first = content_length - r.second;
- r.second = content_length - 1;
+ r.first = (std::max)(static_cast<ssize_t>(0), slen - r.second);
+ r.second = slen - 1;
}
- if (r.second == -1) { r.second = content_length - 1; }
+ if (r.second == -1) { r.second = slen - 1; }
return std::make_pair(r.first, r.second - r.first + 1);
}
@@ -2420,16 +3276,19 @@ get_multipart_ranges_data_length(const Request &req, Response &res,
return data_length;
}
+template <typename T>
inline bool write_multipart_ranges_data(Stream &strm, const Request &req,
Response &res,
const std::string &boundary,
- const std::string &content_type) {
+ const std::string &content_type,
+ T is_shutting_down) {
return process_multipart_ranges_data(
req, res, boundary, content_type,
[&](const std::string &token) { strm.write(token); },
[&](const char *token) { strm.write(token); },
[&](size_t offset, size_t length) {
- return write_content(strm, res.content_provider, offset, length) >= 0;
+ return write_content(strm, res.content_provider_, offset, length,
+ is_shutting_down) >= 0;
});
}
@@ -2438,20 +3297,31 @@ get_range_offset_and_length(const Request &req, const Response &res,
size_t index) {
auto r = req.ranges[index];
- if (r.second == -1) { r.second = res.content_length - 1; }
+ if (r.second == -1) {
+ r.second = static_cast<ssize_t>(res.content_length_) - 1;
+ }
return std::make_pair(r.first, r.second - r.first + 1);
}
inline bool expect_content(const Request &req) {
if (req.method == "POST" || req.method == "PUT" || req.method == "PATCH" ||
- req.method == "PRI") {
+ req.method == "PRI" || req.method == "DELETE") {
return true;
}
// TODO: check if Content-Length is set
return false;
}
+inline bool has_crlf(const char *s) {
+ auto p = s;
+ while (*p) {
+ if (*p == '\r' || *p == '\n') { return true; }
+ p++;
+ }
+ return false;
+}
+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
template <typename CTX, typename Init, typename Update, typename Final>
inline std::string message_digest(const std::string &s, Init init,
@@ -2489,6 +3359,33 @@ inline std::string SHA_512(const std::string &s) {
#endif
#ifdef _WIN32
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+// NOTE: This code came up with the following stackoverflow post:
+// https://stackoverflow.com/questions/9507184/can-openssl-on-windows-use-the-system-certificate-store
+inline bool load_system_certs_on_windows(X509_STORE *store) {
+ auto hStore = CertOpenSystemStoreW((HCRYPTPROV_LEGACY)NULL, L"ROOT");
+
+ if (!hStore) { return false; }
+
+ PCCERT_CONTEXT pContext = NULL;
+ while (pContext = CertEnumCertificatesInStore(hStore, pContext)) {
+ auto encoded_cert =
+ static_cast<const unsigned char *>(pContext->pbCertEncoded);
+
+ auto x509 = d2i_X509(NULL, &encoded_cert, pContext->cbCertEncoded);
+ if (x509) {
+ X509_STORE_add_cert(store, x509);
+ X509_free(x509);
+ }
+ }
+
+ CertFreeCertificateContext(pContext);
+ CertCloseStore(hStore, 0);
+
+ return true;
+}
+#endif
+
class WSInit {
public:
WSInit() {
@@ -2502,31 +3399,6 @@ public:
static WSInit wsinit_;
#endif
-} // namespace detail
-
-// Header utilities
-inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
- std::string field = "bytes=";
- auto i = 0;
- for (auto r : ranges) {
- if (i != 0) { field += ", "; }
- if (r.first != -1) { field += std::to_string(r.first); }
- field += '-';
- if (r.second != -1) { field += std::to_string(r.second); }
- i++;
- }
- return std::make_pair("Range", field);
-}
-
-inline std::pair<std::string, std::string>
-make_basic_authentication_header(const std::string &username,
- const std::string &password,
- bool is_proxy = false) {
- auto field = "Basic " + detail::base64_encode(username + ":" + password);
- auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
- return std::make_pair(key, field);
-}
-
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
inline std::pair<std::string, std::string> make_digest_authentication_header(
const Request &req, const std::map<std::string, std::string> &auth,
@@ -2566,17 +3438,18 @@ inline std::pair<std::string, std::string> make_digest_authentication_header(
":" + qop + ":" + H(A2));
}
- auto field = "Digest username=\"hello\", realm=\"" + auth.at("realm") +
- "\", nonce=\"" + auth.at("nonce") + "\", uri=\"" + req.path +
- "\", algorithm=" + algo + ", qop=" + qop + ", nc=\"" + nc +
- "\", cnonce=\"" + cnonce + "\", response=\"" + response + "\"";
+ auto field = "Digest username=\"" + username + "\", realm=\"" +
+ auth.at("realm") + "\", nonce=\"" + auth.at("nonce") +
+ "\", uri=\"" + req.path + "\", algorithm=" + algo +
+ ", qop=" + qop + ", nc=\"" + nc + "\", cnonce=\"" + cnonce +
+ "\", response=\"" + response + "\"";
auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
return std::make_pair(key, field);
}
#endif
-inline bool parse_www_authenticate(const httplib::Response &res,
+inline bool parse_www_authenticate(const Response &res,
std::map<std::string, std::string> &auth,
bool is_proxy) {
auto auth_key = is_proxy ? "Proxy-Authenticate" : "WWW-Authenticate";
@@ -2593,9 +3466,13 @@ inline bool parse_www_authenticate(const httplib::Response &res,
auto beg = std::sregex_iterator(s.begin(), s.end(), re);
for (auto i = beg; i != std::sregex_iterator(); ++i) {
auto m = *i;
- auto key = s.substr(m.position(1), m.length(1));
- auto val = m.length(2) > 0 ? s.substr(m.position(2), m.length(2))
- : s.substr(m.position(3), m.length(3));
+ auto key = s.substr(static_cast<size_t>(m.position(1)),
+ static_cast<size_t>(m.length(1)));
+ auto val = m.length(2) > 0
+ ? s.substr(static_cast<size_t>(m.position(2)),
+ static_cast<size_t>(m.length(2)))
+ : s.substr(static_cast<size_t>(m.position(3)),
+ static_cast<size_t>(m.length(3)));
auth[key] = val;
}
return true;
@@ -2612,13 +3489,60 @@ inline std::string random_string(size_t length) {
"ABCDEFGHIJKLMNOPQRSTUVWXYZ"
"abcdefghijklmnopqrstuvwxyz";
const size_t max_index = (sizeof(charset) - 1);
- return charset[rand() % max_index];
+ return charset[static_cast<size_t>(rand()) % max_index];
};
std::string str(length, 0);
std::generate_n(str.begin(), length, randchar);
return str;
}
+class ContentProviderAdapter {
+public:
+ explicit ContentProviderAdapter(
+ ContentProviderWithoutLength &&content_provider)
+ : content_provider_(content_provider) {}
+
+ bool operator()(size_t offset, size_t, DataSink &sink) {
+ return content_provider_(offset, sink);
+ }
+
+private:
+ ContentProviderWithoutLength content_provider_;
+};
+
+} // namespace detail
+
+// Header utilities
+inline std::pair<std::string, std::string> make_range_header(Ranges ranges) {
+ std::string field = "bytes=";
+ auto i = 0;
+ for (auto r : ranges) {
+ if (i != 0) { field += ", "; }
+ if (r.first != -1) { field += std::to_string(r.first); }
+ field += '-';
+ if (r.second != -1) { field += std::to_string(r.second); }
+ i++;
+ }
+ return std::make_pair("Range", std::move(field));
+}
+
+inline std::pair<std::string, std::string>
+make_basic_authentication_header(const std::string &username,
+ const std::string &password,
+ bool is_proxy = false) {
+ auto field = "Basic " + detail::base64_encode(username + ":" + password);
+ auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
+ return std::make_pair(key, std::move(field));
+}
+
+inline std::pair<std::string, std::string>
+make_bearer_token_authentication_header(const std::string &token,
+ bool is_proxy = false) {
+ auto field = "Bearer " + token;
+ auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
+ return std::make_pair(key, std::move(field));
+}
+
// Request implementation
inline bool Request::has_header(const char *key) const {
return detail::has_header(headers, key);
@@ -2628,17 +3552,26 @@ inline std::string Request::get_header_value(const char *key, size_t id) const {
return detail::get_header_value(headers, key, id, "");
}
+template <typename T>
+inline T Request::get_header_value(const char *key, size_t id) const {
+ return detail::get_header_value<T>(headers, key, id, 0);
+}
+
inline size_t Request::get_header_value_count(const char *key) const {
auto r = headers.equal_range(key);
- return std::distance(r.first, r.second);
+ return static_cast<size_t>(std::distance(r.first, r.second));
}
inline void Request::set_header(const char *key, const char *val) {
- headers.emplace(key, val);
+ if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
+ headers.emplace(key, val);
+ }
}
inline void Request::set_header(const char *key, const std::string &val) {
- headers.emplace(key, val);
+ if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
+ headers.emplace(key, val);
+ }
}
inline bool Request::has_param(const char *key) const {
@@ -2646,15 +3579,16 @@ inline bool Request::has_param(const char *key) const {
}
inline std::string Request::get_param_value(const char *key, size_t id) const {
- auto it = params.find(key);
- std::advance(it, id);
- if (it != params.end()) { return it->second; }
+ auto rng = params.equal_range(key);
+ auto it = rng.first;
+ std::advance(it, static_cast<ssize_t>(id));
+ if (it != rng.second) { return it->second; }
return std::string();
}
inline size_t Request::get_param_value_count(const char *key) const {
auto r = params.equal_range(key);
- return std::distance(r.first, r.second);
+ return static_cast<size_t>(std::distance(r.first, r.second));
}
inline bool Request::is_multipart_form_data() const {
@@ -2682,22 +3616,41 @@ inline std::string Response::get_header_value(const char *key,
return detail::get_header_value(headers, key, id, "");
}
+template <typename T>
+inline T Response::get_header_value(const char *key, size_t id) const {
+ return detail::get_header_value<T>(headers, key, id, 0);
+}
+
inline size_t Response::get_header_value_count(const char *key) const {
auto r = headers.equal_range(key);
- return std::distance(r.first, r.second);
+ return static_cast<size_t>(std::distance(r.first, r.second));
}
inline void Response::set_header(const char *key, const char *val) {
- headers.emplace(key, val);
+ if (!detail::has_crlf(key) && !detail::has_crlf(val)) {
+ headers.emplace(key, val);
+ }
}
inline void Response::set_header(const char *key, const std::string &val) {
- headers.emplace(key, val);
+ if (!detail::has_crlf(key) && !detail::has_crlf(val.c_str())) {
+ headers.emplace(key, val);
+ }
+}
+
+inline void Response::set_redirect(const char *url, int stat) {
+ if (!detail::has_crlf(url)) {
+ set_header("Location", url);
+ if (300 <= stat && stat < 400) {
+ this->status = stat;
+ } else {
+ this->status = 302;
+ }
+ }
}
-inline void Response::set_redirect(const char *url) {
- set_header("Location", url);
- status = 302;
+inline void Response::set_redirect(const std::string &url, int stat) {
+ set_redirect(url.c_str(), stat);
}
inline void Response::set_content(const char *s, size_t n,
@@ -2706,62 +3659,79 @@ inline void Response::set_content(const char *s, size_t n,
set_header("Content-Type", content_type);
}
-inline void Response::set_content(const std::string &s,
- const char *content_type) {
- body = s;
+inline void Response::set_content(std::string s, const char *content_type) {
+ body = std::move(s);
set_header("Content-Type", content_type);
}
-inline void Response::set_content_provider(
- size_t in_length,
- std::function<void(size_t offset, size_t length, DataSink &sink)> provider,
- std::function<void()> resource_releaser) {
+inline void
+Response::set_content_provider(size_t in_length, const char *content_type,
+ ContentProvider provider,
+ const std::function<void()> &resource_releaser) {
assert(in_length > 0);
- content_length = in_length;
- content_provider = [provider](size_t offset, size_t length, DataSink &sink) {
- provider(offset, length, sink);
- };
- content_provider_resource_releaser = resource_releaser;
+ set_header("Content-Type", content_type);
+ content_length_ = in_length;
+ content_provider_ = std::move(provider);
+ content_provider_resource_releaser_ = resource_releaser;
+ is_chunked_content_provider = false;
+}
+
+inline void
+Response::set_content_provider(const char *content_type,
+ ContentProviderWithoutLength provider,
+ const std::function<void()> &resource_releaser) {
+ set_header("Content-Type", content_type);
+ content_length_ = 0;
+ content_provider_ = detail::ContentProviderAdapter(std::move(provider));
+ content_provider_resource_releaser_ = resource_releaser;
+ is_chunked_content_provider = false;
}
inline void Response::set_chunked_content_provider(
- std::function<void(size_t offset, DataSink &sink)> provider,
- std::function<void()> resource_releaser) {
- content_length = 0;
- content_provider = [provider](size_t offset, size_t, DataSink &sink) {
- provider(offset, sink);
- };
- content_provider_resource_releaser = resource_releaser;
+ const char *content_type, ContentProviderWithoutLength provider,
+ const std::function<void()> &resource_releaser) {
+ set_header("Content-Type", content_type);
+ content_length_ = 0;
+ content_provider_ = detail::ContentProviderAdapter(std::move(provider));
+ content_provider_resource_releaser_ = resource_releaser;
+ is_chunked_content_provider = true;
}
// Rstream implementation
-inline int Stream::write(const char *ptr) { return write(ptr, strlen(ptr)); }
+inline ssize_t Stream::write(const char *ptr) {
+ return write(ptr, strlen(ptr));
+}
-inline int Stream::write(const std::string &s) {
+inline ssize_t Stream::write(const std::string &s) {
return write(s.data(), s.size());
}
template <typename... Args>
-inline int Stream::write_format(const char *fmt, const Args &... args) {
- std::array<char, 2048> buf;
+inline ssize_t Stream::write_format(const char *fmt, const Args &... args) {
+ const auto bufsiz = 2048;
+ std::array<char, bufsiz> buf;
#if defined(_MSC_VER) && _MSC_VER < 1900
- auto n = _snprintf_s(buf, bufsiz, buf.size() - 1, fmt, args...);
+ auto sn = _snprintf_s(buf.data(), bufsiz - 1, buf.size() - 1, fmt, args...);
#else
- auto n = snprintf(buf.data(), buf.size() - 1, fmt, args...);
+ auto sn = snprintf(buf.data(), buf.size() - 1, fmt, args...);
#endif
- if (n <= 0) { return n; }
+ if (sn <= 0) { return sn; }
- if (n >= static_cast<int>(buf.size()) - 1) {
+ auto n = static_cast<size_t>(sn);
+
+ if (n >= buf.size() - 1) {
std::vector<char> glowable_buf(buf.size());
- while (n >= static_cast<int>(glowable_buf.size() - 1)) {
+ while (n >= glowable_buf.size() - 1) {
glowable_buf.resize(glowable_buf.size() * 2);
#if defined(_MSC_VER) && _MSC_VER < 1900
- n = _snprintf_s(&glowable_buf[0], glowable_buf.size(),
- glowable_buf.size() - 1, fmt, args...);
+ n = static_cast<size_t>(_snprintf_s(&glowable_buf[0], glowable_buf.size(),
+ glowable_buf.size() - 1, fmt,
+ args...));
#else
- n = snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...);
+ n = static_cast<size_t>(
+ snprintf(&glowable_buf[0], glowable_buf.size() - 1, fmt, args...));
#endif
}
return write(&glowable_buf[0], n);
@@ -2774,32 +3744,53 @@ namespace detail {
// Socket stream implementation
inline SocketStream::SocketStream(socket_t sock, time_t read_timeout_sec,
- time_t read_timeout_usec)
+ time_t read_timeout_usec,
+ time_t write_timeout_sec,
+ time_t write_timeout_usec)
: sock_(sock), read_timeout_sec_(read_timeout_sec),
- read_timeout_usec_(read_timeout_usec) {}
+ read_timeout_usec_(read_timeout_usec),
+ write_timeout_sec_(write_timeout_sec),
+ write_timeout_usec_(write_timeout_usec) {}
inline SocketStream::~SocketStream() {}
inline bool SocketStream::is_readable() const {
- return detail::select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
+ return select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0;
}
inline bool SocketStream::is_writable() const {
- return detail::select_write(sock_, 0, 0) > 0;
+ return select_write(sock_, write_timeout_sec_, write_timeout_usec_) > 0;
}
-inline int SocketStream::read(char *ptr, size_t size) {
- if (is_readable()) { return recv(sock_, ptr, static_cast<int>(size), 0); }
- return -1;
+inline ssize_t SocketStream::read(char *ptr, size_t size) {
+ if (!is_readable()) { return -1; }
+
+#ifdef _WIN32
+ if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
+ return -1;
+ }
+ return recv(sock_, ptr, static_cast<int>(size), 0);
+#else
+ return handle_EINTR([&]() { return recv(sock_, ptr, size, 0); });
+#endif
}
-inline int SocketStream::write(const char *ptr, size_t size) {
- if (is_writable()) { return send(sock_, ptr, static_cast<int>(size), 0); }
- return -1;
+inline ssize_t SocketStream::write(const char *ptr, size_t size) {
+ if (!is_writable()) { return -1; }
+
+#ifdef _WIN32
+ if (size > static_cast<size_t>((std::numeric_limits<int>::max)())) {
+ return -1;
+ }
+ return send(sock_, ptr, static_cast<int>(size), 0);
+#else
+ return handle_EINTR([&]() { return send(sock_, ptr, size, 0); });
+#endif
}
-inline std::string SocketStream::get_remote_addr() const {
- return detail::get_remote_addr(sock_);
+inline void SocketStream::get_remote_ip_and_port(std::string &ip,
+ int &port) const {
+ return detail::get_remote_ip_and_port(sock_, ip, port);
}
// Buffer stream implementation
@@ -2807,22 +3798,23 @@ inline bool BufferStream::is_readable() const { return true; }
inline bool BufferStream::is_writable() const { return true; }
-inline int BufferStream::read(char *ptr, size_t size) {
-#if defined(_MSC_VER) && _MSC_VER < 1900
- int len_read = static_cast<int>(buffer._Copy_s(ptr, size, size, position));
+inline ssize_t BufferStream::read(char *ptr, size_t size) {
+#if defined(_MSC_VER) && _MSC_VER <= 1900
+ auto len_read = buffer._Copy_s(ptr, size, size, position);
#else
- int len_read = static_cast<int>(buffer.copy(ptr, size, position));
+ auto len_read = buffer.copy(ptr, size, position);
#endif
- position += len_read;
- return len_read;
+ position += static_cast<size_t>(len_read);
+ return static_cast<ssize_t>(len_read);
}
-inline int BufferStream::write(const char *ptr, size_t size) {
+inline ssize_t BufferStream::write(const char *ptr, size_t size) {
buffer.append(ptr, size);
- return static_cast<int>(size);
+ return static_cast<ssize_t>(size);
}
-inline std::string BufferStream::get_remote_addr() const { return ""; }
+inline void BufferStream::get_remote_ip_and_port(std::string & /*ip*/,
+ int & /*port*/) const {}
inline const std::string &BufferStream::get_buffer() const { return buffer; }
@@ -2830,67 +3822,77 @@ inline const std::string &BufferStream::get_buffer() const { return buffer; }
// HTTP server implementation
inline Server::Server()
- : keep_alive_max_count_(CPPHTTPLIB_KEEPALIVE_MAX_COUNT),
- read_timeout_sec_(CPPHTTPLIB_READ_TIMEOUT_SECOND),
- read_timeout_usec_(CPPHTTPLIB_READ_TIMEOUT_USECOND),
- payload_max_length_(CPPHTTPLIB_PAYLOAD_MAX_LENGTH), is_running_(false),
- svr_sock_(INVALID_SOCKET) {
+ : new_task_queue(
+ [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); }),
+ svr_sock_(INVALID_SOCKET), is_running_(false) {
#ifndef _WIN32
signal(SIGPIPE, SIG_IGN);
#endif
- new_task_queue = [] { return new ThreadPool(CPPHTTPLIB_THREAD_POOL_COUNT); };
}
inline Server::~Server() {}
inline Server &Server::Get(const char *pattern, Handler handler) {
- get_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
+ get_handlers_.push_back(
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Post(const char *pattern, Handler handler) {
- post_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
+ post_handlers_.push_back(
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Post(const char *pattern,
HandlerWithContentReader handler) {
post_handlers_for_content_reader_.push_back(
- std::make_pair(std::regex(pattern), handler));
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Put(const char *pattern, Handler handler) {
- put_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
+ put_handlers_.push_back(
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Put(const char *pattern,
HandlerWithContentReader handler) {
put_handlers_for_content_reader_.push_back(
- std::make_pair(std::regex(pattern), handler));
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Patch(const char *pattern, Handler handler) {
- patch_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
+ patch_handlers_.push_back(
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Patch(const char *pattern,
HandlerWithContentReader handler) {
patch_handlers_for_content_reader_.push_back(
- std::make_pair(std::regex(pattern), handler));
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Delete(const char *pattern, Handler handler) {
- delete_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
+ delete_handlers_.push_back(
+ std::make_pair(std::regex(pattern), std::move(handler)));
+ return *this;
+}
+
+inline Server &Server::Delete(const char *pattern,
+ HandlerWithContentReader handler) {
+ delete_handlers_for_content_reader_.push_back(
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
inline Server &Server::Options(const char *pattern, Handler handler) {
- options_handlers_.push_back(std::make_pair(std::regex(pattern), handler));
+ options_handlers_.push_back(
+ std::make_pair(std::regex(pattern), std::move(handler)));
return *this;
}
@@ -2898,11 +3900,12 @@ inline bool Server::set_base_dir(const char *dir, const char *mount_point) {
return set_mount_point(mount_point, dir);
}
-inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
+inline bool Server::set_mount_point(const char *mount_point, const char *dir,
+ Headers headers) {
if (detail::is_dir(dir)) {
std::string mnt = mount_point ? mount_point : "/";
if (!mnt.empty() && mnt[0] == '/') {
- base_dirs_.emplace_back(mnt, dir);
+ base_dirs_.push_back({mnt, dir, std::move(headers)});
return true;
}
}
@@ -2911,7 +3914,7 @@ inline bool Server::set_mount_point(const char *mount_point, const char *dir) {
inline bool Server::remove_mount_point(const char *mount_point) {
for (auto it = base_dirs_.begin(); it != base_dirs_.end(); ++it) {
- if (it->first == mount_point) {
+ if (it->mount_point == mount_point) {
base_dirs_.erase(it);
return true;
}
@@ -2932,6 +3935,12 @@ inline void Server::set_error_handler(Handler handler) {
error_handler_ = std::move(handler);
}
+inline void Server::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
+
+inline void Server::set_socket_options(SocketOptions socket_options) {
+ socket_options_ = std::move(socket_options);
+}
+
inline void Server::set_logger(Logger logger) { logger_ = std::move(logger); }
inline void
@@ -2943,11 +3952,25 @@ inline void Server::set_keep_alive_max_count(size_t count) {
keep_alive_max_count_ = count;
}
+inline void Server::set_keep_alive_timeout(time_t sec) {
+ keep_alive_timeout_sec_ = sec;
+}
+
inline void Server::set_read_timeout(time_t sec, time_t usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
}
+inline void Server::set_write_timeout(time_t sec, time_t usec) {
+ write_timeout_sec_ = sec;
+ write_timeout_usec_ = usec;
+}
+
+inline void Server::set_idle_interval(time_t sec, time_t usec) {
+ idle_interval_sec_ = sec;
+ idle_interval_usec_ = usec;
+}
+
inline void Server::set_payload_max_length(size_t length) {
payload_max_length_ = length;
}
@@ -2987,7 +4010,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
req.version = std::string(m[5]);
req.method = std::string(m[1]);
req.target = std::string(m[2]);
- req.path = detail::decode_url(m[3]);
+ req.path = detail::decode_url(m[3], false);
// Parse query text
auto len = std::distance(m[4].first, m[4].second);
@@ -2999,7 +4022,7 @@ inline bool Server::parse_request_line(const char *s, Request &req) {
return false;
}
-inline bool Server::write_response(Stream &strm, bool last_connection,
+inline bool Server::write_response(Stream &strm, bool close_connection,
const Request &req, Response &res) {
assert(res.status != -1);
@@ -3014,16 +4037,17 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
}
// Headers
- if (last_connection || req.get_header_value("Connection") == "close") {
+ if (close_connection || req.get_header_value("Connection") == "close") {
res.set_header("Connection", "close");
- }
-
- if (!last_connection && req.get_header_value("Connection") == "Keep-Alive") {
- res.set_header("Connection", "Keep-Alive");
+ } else {
+ std::stringstream ss;
+ ss << "timeout=" << keep_alive_timeout_sec_
+ << ", max=" << keep_alive_max_count_;
+ res.set_header("Keep-Alive", ss.str());
}
if (!res.has_header("Content-Type") &&
- (!res.body.empty() || res.content_length > 0)) {
+ (!res.body.empty() || res.content_length_ > 0 || res.content_provider_)) {
res.set_header("Content-Type", "text/plain");
}
@@ -3047,18 +4071,20 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
"multipart/byteranges; boundary=" + boundary);
}
+ auto type = detail::encoding_type(req, res);
+
if (res.body.empty()) {
- if (res.content_length > 0) {
+ if (res.content_length_ > 0) {
size_t length = 0;
if (req.ranges.empty()) {
- length = res.content_length;
+ length = res.content_length_;
} else if (req.ranges.size() == 1) {
auto offsets =
- detail::get_range_offset_and_length(req, res.content_length, 0);
+ detail::get_range_offset_and_length(req, res.content_length_, 0);
auto offset = offsets.first;
length = offsets.second;
auto content_range = detail::make_content_range_header_field(
- offset, length, res.content_length);
+ offset, length, res.content_length_);
res.set_header("Content-Range", content_range);
} else {
length = detail::get_multipart_ranges_data_length(req, res, boundary,
@@ -3066,8 +4092,15 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
}
res.set_header("Content-Length", std::to_string(length));
} else {
- if (res.content_provider) {
- res.set_header("Transfer-Encoding", "chunked");
+ if (res.content_provider_) {
+ if (res.is_chunked_content_provider) {
+ res.set_header("Transfer-Encoding", "chunked");
+ if (type == detail::EncodingType::Gzip) {
+ res.set_header("Content-Encoding", "gzip");
+ } else if (type == detail::EncodingType::Brotli) {
+ res.set_header("Content-Encoding", "br");
+ }
+ }
} else {
res.set_header("Content-Length", "0");
}
@@ -3089,16 +4122,35 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
detail::make_multipart_ranges_data(req, res, boundary, content_type);
}
+ if (type != detail::EncodingType::None) {
+ std::unique_ptr<detail::compressor> compressor;
+
+ if (type == detail::EncodingType::Gzip) {
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
- // TODO: 'Accept-Encoding' has gzip, not gzip;q=0
- const auto &encodings = req.get_header_value("Accept-Encoding");
- if (encodings.find("gzip") != std::string::npos &&
- detail::can_compress(res.get_header_value("Content-Type"))) {
- if (detail::compress(res.body)) {
+ compressor = detail::make_unique<detail::gzip_compressor>();
res.set_header("Content-Encoding", "gzip");
+#endif
+ } else if (type == detail::EncodingType::Brotli) {
+#ifdef CPPHTTPLIB_BROTLI_SUPPORT
+ compressor = detail::make_unique<detail::brotli_compressor>();
+ res.set_header("Content-Encoding", "brotli");
+#endif
+ }
+
+ if (compressor) {
+ std::string compressed;
+
+ if (!compressor->compress(res.body.data(), res.body.size(), true,
+ [&](const char *data, size_t data_len) {
+ compressed.append(data, data_len);
+ return true;
+ })) {
+ return false;
+ }
+
+ res.body.swap(compressed);
}
}
-#endif
auto length = std::to_string(res.body.size());
res.set_header("Content-Length", length);
@@ -3111,13 +4163,14 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
strm.write(data.data(), data.size());
// Body
+ auto ret = true;
if (req.method != "HEAD") {
if (!res.body.empty()) {
- if (!strm.write(res.body)) { return false; }
- } else if (res.content_provider) {
+ if (!strm.write(res.body)) { ret = false; }
+ } else if (res.content_provider_) {
if (!write_content_with_provider(strm, req, res, boundary,
content_type)) {
- return false;
+ ret = false;
}
}
}
@@ -3125,119 +4178,158 @@ inline bool Server::write_response(Stream &strm, bool last_connection,
// Log
if (logger_) { logger_(req, res); }
- return true;
+ return ret;
}
inline bool
Server::write_content_with_provider(Stream &strm, const Request &req,
Response &res, const std::string &boundary,
const std::string &content_type) {
- if (res.content_length) {
+ auto is_shutting_down = [this]() {
+ return this->svr_sock_ == INVALID_SOCKET;
+ };
+
+ if (res.content_length_ > 0) {
if (req.ranges.empty()) {
- if (detail::write_content(strm, res.content_provider, 0,
- res.content_length) < 0) {
+ if (detail::write_content(strm, res.content_provider_, 0,
+ res.content_length_, is_shutting_down) < 0) {
return false;
}
} else if (req.ranges.size() == 1) {
auto offsets =
- detail::get_range_offset_and_length(req, res.content_length, 0);
+ detail::get_range_offset_and_length(req, res.content_length_, 0);
auto offset = offsets.first;
auto length = offsets.second;
- if (detail::write_content(strm, res.content_provider, offset, length) <
- 0) {
+ if (detail::write_content(strm, res.content_provider_, offset, length,
+ is_shutting_down) < 0) {
return false;
}
} else {
- if (!detail::write_multipart_ranges_data(strm, req, res, boundary,
- content_type)) {
+ if (!detail::write_multipart_ranges_data(
+ strm, req, res, boundary, content_type, is_shutting_down)) {
return false;
}
}
} else {
- auto is_shutting_down = [this]() {
- return this->svr_sock_ == INVALID_SOCKET;
- };
- if (detail::write_content_chunked(strm, res.content_provider,
- is_shutting_down) < 0) {
- return false;
+ if (res.is_chunked_content_provider) {
+ auto type = detail::encoding_type(req, res);
+
+ std::unique_ptr<detail::compressor> compressor;
+ if (type == detail::EncodingType::Gzip) {
+#ifdef CPPHTTPLIB_ZLIB_SUPPORT
+ compressor = detail::make_unique<detail::gzip_compressor>();
+#endif
+ } else if (type == detail::EncodingType::Brotli) {
+#ifdef CPPHTTPLIB_BROTLI_SUPPORT
+ compressor = detail::make_unique<detail::brotli_compressor>();
+#endif
+ } else {
+ compressor = detail::make_unique<detail::nocompressor>();
+ }
+ assert(compressor != nullptr);
+
+ if (detail::write_content_chunked(strm, res.content_provider_,
+ is_shutting_down, *compressor) < 0) {
+ return false;
+ }
+ } else {
+ if (detail::write_content_without_length(strm, res.content_provider_,
+ is_shutting_down) < 0) {
+ return false;
+ }
}
}
return true;
}
-inline bool Server::read_content(Stream &strm, bool last_connection,
- Request &req, Response &res) {
+inline bool Server::read_content(Stream &strm, Request &req, Response &res) {
MultipartFormDataMap::iterator cur;
- auto ret = read_content_core(
- strm, last_connection, req, res,
- // Regular
- [&](const char *buf, size_t n) {
- if (req.body.size() + n > req.body.max_size()) { return false; }
- req.body.append(buf, n);
- return true;
- },
- // Multipart
- [&](const MultipartFormData &file) {
- cur = req.files.emplace(file.name, file);
- return true;
- },
- [&](const char *buf, size_t n) {
- auto &content = cur->second.content;
- if (content.size() + n > content.max_size()) { return false; }
- content.append(buf, n);
- return true;
- });
-
- const auto &content_type = req.get_header_value("Content-Type");
- if (!content_type.find("application/x-www-form-urlencoded")) {
- detail::parse_query_text(req.body, req.params);
+ if (read_content_core(
+ strm, req, res,
+ // Regular
+ [&](const char *buf, size_t n) {
+ if (req.body.size() + n > req.body.max_size()) { return false; }
+ req.body.append(buf, n);
+ return true;
+ },
+ // Multipart
+ [&](const MultipartFormData &file) {
+ cur = req.files.emplace(file.name, file);
+ return true;
+ },
+ [&](const char *buf, size_t n) {
+ auto &content = cur->second.content;
+ if (content.size() + n > content.max_size()) { return false; }
+ content.append(buf, n);
+ return true;
+ })) {
+ const auto &content_type = req.get_header_value("Content-Type");
+ if (!content_type.find("application/x-www-form-urlencoded")) {
+ detail::parse_query_text(req.body, req.params);
+ }
+ return true;
}
-
- return ret;
+ return false;
}
inline bool Server::read_content_with_content_receiver(
- Stream &strm, bool last_connection, Request &req, Response &res,
- ContentReceiver receiver, MultipartContentHeader multipart_header,
+ Stream &strm, Request &req, Response &res, ContentReceiver receiver,
+ MultipartContentHeader multipart_header,
ContentReceiver multipart_receiver) {
- return read_content_core(strm, last_connection, req, res, receiver,
- multipart_header, multipart_receiver);
+ return read_content_core(strm, req, res, std::move(receiver),
+ std::move(multipart_header),
+ std::move(multipart_receiver));
}
-inline bool Server::read_content_core(Stream &strm, bool last_connection,
- Request &req, Response &res,
+inline bool Server::read_content_core(Stream &strm, Request &req, Response &res,
ContentReceiver receiver,
MultipartContentHeader mulitpart_header,
ContentReceiver multipart_receiver) {
detail::MultipartFormDataParser multipart_form_data_parser;
- ContentReceiver out;
+ ContentReceiverWithProgress out;
if (req.is_multipart_form_data()) {
const auto &content_type = req.get_header_value("Content-Type");
std::string boundary;
if (!detail::parse_multipart_boundary(content_type, boundary)) {
res.status = 400;
- return write_response(strm, last_connection, req, res);
+ return false;
}
- multipart_form_data_parser.set_boundary(boundary);
- out = [&](const char *buf, size_t n) {
+ multipart_form_data_parser.set_boundary(std::move(boundary));
+ out = [&](const char *buf, size_t n, uint64_t /*off*/, uint64_t /*len*/) {
+ /* For debug
+ size_t pos = 0;
+ while (pos < n) {
+ auto read_size = std::min<size_t>(1, n - pos);
+ auto ret = multipart_form_data_parser.parse(
+ buf + pos, read_size, multipart_receiver, mulitpart_header);
+ if (!ret) { return false; }
+ pos += read_size;
+ }
+ return true;
+ */
return multipart_form_data_parser.parse(buf, n, multipart_receiver,
mulitpart_header);
};
} else {
- out = receiver;
+ out = [receiver](const char *buf, size_t n, uint64_t /*off*/,
+ uint64_t /*len*/) { return receiver(buf, n); };
}
- if (!detail::read_content(strm, req, payload_max_length_, res.status,
- Progress(), out)) {
- return write_response(strm, last_connection, req, res);
+ if (req.method == "DELETE" && !req.has_header("Content-Length")) {
+ return true;
+ }
+
+ if (!detail::read_content(strm, req, payload_max_length_, res.status, nullptr,
+ out, true)) {
+ return false;
}
if (req.is_multipart_form_data()) {
if (!multipart_form_data_parser.is_valid()) {
res.status = 400;
- return write_response(strm, last_connection, req, res);
+ return false;
}
}
@@ -3246,15 +4338,12 @@ inline bool Server::read_content_core(Stream &strm, bool last_connection,
inline bool Server::handle_file_request(Request &req, Response &res,
bool head) {
- for (const auto &kv : base_dirs_) {
- const auto &mount_point = kv.first;
- const auto &base_dir = kv.second;
-
+ for (const auto &entry : base_dirs_) {
// Prefix match
- if (!req.path.find(mount_point)) {
- std::string sub_path = "/" + req.path.substr(mount_point.size());
+ if (!req.path.compare(0, entry.mount_point.size(), entry.mount_point)) {
+ std::string sub_path = "/" + req.path.substr(entry.mount_point.size());
if (detail::is_valid_path(sub_path)) {
- auto path = base_dir + sub_path;
+ auto path = entry.base_dir + sub_path;
if (path.back() == '/') { path += "index.html"; }
if (detail::is_file(path)) {
@@ -3262,6 +4351,9 @@ inline bool Server::handle_file_request(Request &req, Response &res,
auto type =
detail::find_content_type(path, file_extension_and_mimetype_map_);
if (type) { res.set_header("Content-Type", type); }
+ for (const auto &kv : entry.headers) {
+ res.set_header(kv.first.c_str(), kv.second);
+ }
res.status = 200;
if (!head && file_request_handler_) {
file_request_handler_(req, res);
@@ -3274,40 +4366,39 @@ inline bool Server::handle_file_request(Request &req, Response &res,
return false;
}
-inline socket_t Server::create_server_socket(const char *host, int port,
- int socket_flags) const {
+inline socket_t
+Server::create_server_socket(const char *host, int port, int socket_flags,
+ SocketOptions socket_options) const {
return detail::create_socket(
- host, port,
+ host, port, socket_flags, tcp_nodelay_, std::move(socket_options),
[](socket_t sock, struct addrinfo &ai) -> bool {
- if (::bind(sock, ai.ai_addr, static_cast<int>(ai.ai_addrlen))) {
+ if (::bind(sock, ai.ai_addr, static_cast<socklen_t>(ai.ai_addrlen))) {
return false;
}
if (::listen(sock, 5)) { // Listen through 5 channels
return false;
}
return true;
- },
- socket_flags);
+ });
}
inline int Server::bind_internal(const char *host, int port, int socket_flags) {
if (!is_valid()) { return -1; }
- svr_sock_ = create_server_socket(host, port, socket_flags);
+ svr_sock_ = create_server_socket(host, port, socket_flags, socket_options_);
if (svr_sock_ == INVALID_SOCKET) { return -1; }
if (port == 0) {
- struct sockaddr_storage address;
- socklen_t len = sizeof(address);
- if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&address),
- &len) == -1) {
+ struct sockaddr_storage addr;
+ socklen_t addr_len = sizeof(addr);
+ if (getsockname(svr_sock_, reinterpret_cast<struct sockaddr *>(&addr),
+ &addr_len) == -1) {
return -1;
}
- if (address.ss_family == AF_INET) {
- return ntohs(reinterpret_cast<struct sockaddr_in *>(&address)->sin_port);
- } else if (address.ss_family == AF_INET6) {
- return ntohs(
- reinterpret_cast<struct sockaddr_in6 *>(&address)->sin6_port);
+ if (addr.ss_family == AF_INET) {
+ return ntohs(reinterpret_cast<struct sockaddr_in *>(&addr)->sin_port);
+ } else if (addr.ss_family == AF_INET6) {
+ return ntohs(reinterpret_cast<struct sockaddr_in6 *>(&addr)->sin6_port);
} else {
return -1;
}
@@ -3323,18 +4414,19 @@ inline bool Server::listen_internal() {
{
std::unique_ptr<TaskQueue> task_queue(new_task_queue());
- for (;;) {
- if (svr_sock_ == INVALID_SOCKET) {
- // The server socket was closed by 'stop' method.
- break;
- }
-
- auto val = detail::select_read(svr_sock_, 0, 100000);
-
- if (val == 0) { // Timeout
- continue;
+ while (svr_sock_ != INVALID_SOCKET) {
+#ifndef _WIN32
+ if (idle_interval_sec_ > 0 || idle_interval_usec_ > 0) {
+#endif
+ auto val = detail::select_read(svr_sock_, idle_interval_sec_,
+ idle_interval_usec_);
+ if (val == 0) { // Timeout
+ task_queue->on_idle();
+ continue;
+ }
+#ifndef _WIN32
}
-
+#endif
socket_t sock = accept(svr_sock_, nullptr, nullptr);
if (sock == INVALID_SOCKET) {
@@ -3353,7 +4445,11 @@ inline bool Server::listen_internal() {
break;
}
+#if __cplusplus > 201703L
+ task_queue->enqueue([=, this]() { process_and_close_socket(sock); });
+#else
task_queue->enqueue([=]() { process_and_close_socket(sock); });
+#endif
}
task_queue->shutdown();
@@ -3363,8 +4459,7 @@ inline bool Server::listen_internal() {
return ret;
}
-inline bool Server::routing(Request &req, Response &res, Stream &strm,
- bool last_connection) {
+inline bool Server::routing(Request &req, Response &res, Stream &strm) {
// File handler
bool is_head_request = req.method == "HEAD";
if ((req.method == "GET" || is_head_request) &&
@@ -3378,33 +4473,43 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
ContentReader reader(
[&](ContentReceiver receiver) {
return read_content_with_content_receiver(
- strm, last_connection, req, res, receiver, nullptr, nullptr);
+ strm, req, res, std::move(receiver), nullptr, nullptr);
},
[&](MultipartContentHeader header, ContentReceiver receiver) {
- return read_content_with_content_receiver(
- strm, last_connection, req, res, nullptr, header, receiver);
+ return read_content_with_content_receiver(strm, req, res, nullptr,
+ std::move(header),
+ std::move(receiver));
});
if (req.method == "POST") {
if (dispatch_request_for_content_reader(
- req, res, reader, post_handlers_for_content_reader_)) {
+ req, res, std::move(reader),
+ post_handlers_for_content_reader_)) {
return true;
}
} else if (req.method == "PUT") {
if (dispatch_request_for_content_reader(
- req, res, reader, put_handlers_for_content_reader_)) {
+ req, res, std::move(reader),
+ put_handlers_for_content_reader_)) {
return true;
}
} else if (req.method == "PATCH") {
if (dispatch_request_for_content_reader(
- req, res, reader, patch_handlers_for_content_reader_)) {
+ req, res, std::move(reader),
+ patch_handlers_for_content_reader_)) {
+ return true;
+ }
+ } else if (req.method == "DELETE") {
+ if (dispatch_request_for_content_reader(
+ req, res, std::move(reader),
+ delete_handlers_for_content_reader_)) {
return true;
}
}
}
// Read content into `req.body`
- if (!read_content(strm, last_connection, req, res)) { return false; }
+ if (!read_content(strm, req, res)) { return false; }
}
// Regular handler
@@ -3427,22 +4532,30 @@ inline bool Server::routing(Request &req, Response &res, Stream &strm,
}
inline bool Server::dispatch_request(Request &req, Response &res,
- Handlers &handlers) {
- for (const auto &x : handlers) {
- const auto &pattern = x.first;
- const auto &handler = x.second;
-
- if (std::regex_match(req.path, req.matches, pattern)) {
- handler(req, res);
- return true;
+ const Handlers &handlers) {
+ try {
+ for (const auto &x : handlers) {
+ const auto &pattern = x.first;
+ const auto &handler = x.second;
+
+ if (std::regex_match(req.path, req.matches, pattern)) {
+ handler(req, res);
+ return true;
+ }
}
+ } catch (const std::exception &ex) {
+ res.status = 500;
+ res.set_header("EXCEPTION_WHAT", ex.what());
+ } catch (...) {
+ res.status = 500;
+ res.set_header("EXCEPTION_WHAT", "UNKNOWN");
}
return false;
}
inline bool Server::dispatch_request_for_content_reader(
Request &req, Response &res, ContentReader content_reader,
- HandlersForContentReader &handlers) {
+ const HandlersForContentReader &handlers) {
for (const auto &x : handlers) {
const auto &pattern = x.first;
const auto &handler = x.second;
@@ -3456,8 +4569,8 @@ inline bool Server::dispatch_request_for_content_reader(
}
inline bool
-Server::process_request(Stream &strm, bool last_connection,
- bool &connection_close,
+Server::process_request(Stream &strm, bool close_connection,
+ bool &connection_closed,
const std::function<void(Request &)> &setup_request) {
std::array<char, 2048> buf{};
@@ -3476,31 +4589,34 @@ Server::process_request(Stream &strm, bool last_connection,
Headers dummy;
detail::read_headers(strm, dummy);
res.status = 414;
- return write_response(strm, last_connection, req, res);
+ return write_response(strm, close_connection, req, res);
}
// Request line and headers
if (!parse_request_line(line_reader.ptr(), req) ||
!detail::read_headers(strm, req.headers)) {
res.status = 400;
- return write_response(strm, last_connection, req, res);
+ return write_response(strm, close_connection, req, res);
}
if (req.get_header_value("Connection") == "close") {
- connection_close = true;
+ connection_closed = true;
}
if (req.version == "HTTP/1.0" &&
req.get_header_value("Connection") != "Keep-Alive") {
- connection_close = true;
+ connection_closed = true;
}
- req.set_header("REMOTE_ADDR", strm.get_remote_addr());
+ strm.get_remote_ip_and_port(req.remote_addr, req.remote_port);
+ req.set_header("REMOTE_ADDR", req.remote_addr);
+ req.set_header("REMOTE_PORT", std::to_string(req.remote_port));
if (req.has_header("Range")) {
const auto &range_header_value = req.get_header_value("Range");
if (!detail::parse_range_header(range_header_value, req.ranges)) {
- // TODO: error
+ res.status = 416;
+ return write_response(strm, close_connection, req, res);
}
}
@@ -3517,136 +4633,278 @@ Server::process_request(Stream &strm, bool last_connection,
strm.write_format("HTTP/1.1 %d %s\r\n\r\n", status,
detail::status_message(status));
break;
- default: return write_response(strm, last_connection, req, res);
+ default: return write_response(strm, close_connection, req, res);
}
}
// Rounting
- if (routing(req, res, strm, last_connection)) {
+ if (routing(req, res, strm)) {
if (res.status == -1) { res.status = req.ranges.empty() ? 200 : 206; }
} else {
if (res.status == -1) { res.status = 404; }
}
- return write_response(strm, last_connection, req, res);
+ return write_response(strm, close_connection, req, res);
}
inline bool Server::is_valid() const { return true; }
inline bool Server::process_and_close_socket(socket_t sock) {
- return detail::process_and_close_socket(
- false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_,
- [this](Stream &strm, bool last_connection, bool &connection_close) {
- return process_request(strm, last_connection, connection_close,
+ auto ret = detail::process_server_socket(
+ sock, keep_alive_max_count_, keep_alive_timeout_sec_, read_timeout_sec_,
+ read_timeout_usec_, write_timeout_sec_, write_timeout_usec_,
+ [this](Stream &strm, bool close_connection, bool &connection_closed) {
+ return process_request(strm, close_connection, connection_closed,
nullptr);
});
+
+ detail::shutdown_socket(sock);
+ detail::close_socket(sock);
+ return ret;
}
// HTTP client implementation
-inline Client::Client(const std::string &host, int port,
- const std::string &client_cert_path,
- const std::string &client_key_path)
- : host_(host), port_(port),
+inline ClientImpl::ClientImpl(const std::string &host)
+ : ClientImpl(host, 80, std::string(), std::string()) {}
+
+inline ClientImpl::ClientImpl(const std::string &host, int port)
+ : ClientImpl(host, port, std::string(), std::string()) {}
+
+inline ClientImpl::ClientImpl(const std::string &host, int port,
+ const std::string &client_cert_path,
+ const std::string &client_key_path)
+ : error_(Error::Success), host_(host), port_(port),
host_and_port_(host_ + ":" + std::to_string(port_)),
client_cert_path_(client_cert_path), client_key_path_(client_key_path) {}
-inline Client::~Client() {}
+inline ClientImpl::~ClientImpl() { lock_socket_and_shutdown_and_close(); }
+
+inline bool ClientImpl::is_valid() const { return true; }
-inline bool Client::is_valid() const { return true; }
+inline Error ClientImpl::get_last_error() const { return error_; }
-inline socket_t Client::create_client_socket() const {
- if (!proxy_host_.empty()) {
- return detail::create_client_socket(proxy_host_.c_str(), proxy_port_,
- timeout_sec_, interface_);
+inline void ClientImpl::copy_settings(const ClientImpl &rhs) {
+ client_cert_path_ = rhs.client_cert_path_;
+ client_key_path_ = rhs.client_key_path_;
+ connection_timeout_sec_ = rhs.connection_timeout_sec_;
+ read_timeout_sec_ = rhs.read_timeout_sec_;
+ read_timeout_usec_ = rhs.read_timeout_usec_;
+ write_timeout_sec_ = rhs.write_timeout_sec_;
+ write_timeout_usec_ = rhs.write_timeout_usec_;
+ basic_auth_username_ = rhs.basic_auth_username_;
+ basic_auth_password_ = rhs.basic_auth_password_;
+ bearer_token_auth_token_ = rhs.bearer_token_auth_token_;
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ digest_auth_username_ = rhs.digest_auth_username_;
+ digest_auth_password_ = rhs.digest_auth_password_;
+#endif
+ keep_alive_ = rhs.keep_alive_;
+ follow_location_ = rhs.follow_location_;
+ tcp_nodelay_ = rhs.tcp_nodelay_;
+ socket_options_ = rhs.socket_options_;
+ compress_ = rhs.compress_;
+ decompress_ = rhs.decompress_;
+ interface_ = rhs.interface_;
+ proxy_host_ = rhs.proxy_host_;
+ proxy_port_ = rhs.proxy_port_;
+ proxy_basic_auth_username_ = rhs.proxy_basic_auth_username_;
+ proxy_basic_auth_password_ = rhs.proxy_basic_auth_password_;
+ proxy_bearer_token_auth_token_ = rhs.proxy_bearer_token_auth_token_;
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ proxy_digest_auth_username_ = rhs.proxy_digest_auth_username_;
+ proxy_digest_auth_password_ = rhs.proxy_digest_auth_password_;
+#endif
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ server_certificate_verification_ = rhs.server_certificate_verification_;
+#endif
+ logger_ = rhs.logger_;
+}
+
+inline socket_t ClientImpl::create_client_socket() const {
+ if (!proxy_host_.empty() && proxy_port_ != -1) {
+ return detail::create_client_socket(
+ proxy_host_.c_str(), proxy_port_, tcp_nodelay_, socket_options_,
+ connection_timeout_sec_, connection_timeout_usec_, interface_, error_);
}
- return detail::create_client_socket(host_.c_str(), port_, timeout_sec_,
- interface_);
+ return detail::create_client_socket(
+ host_.c_str(), port_, tcp_nodelay_, socket_options_,
+ connection_timeout_sec_, connection_timeout_usec_, interface_, error_);
+}
+
+inline bool ClientImpl::create_and_connect_socket(Socket &socket) {
+ auto sock = create_client_socket();
+ if (sock == INVALID_SOCKET) { return false; }
+ socket.sock = sock;
+ return true;
+}
+
+inline void ClientImpl::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
+ (void)socket;
+ (void)shutdown_gracefully;
+ //If there are any requests in flight from threads other than us, then it's
+ //a thread-unsafe race because individual ssl* objects are not thread-safe.
+ assert(socket_requests_in_flight_ == 0 ||
+ socket_requests_are_from_thread_ == std::this_thread::get_id());
+}
+
+inline void ClientImpl::shutdown_socket(Socket &socket) {
+ if (socket.sock == INVALID_SOCKET)
+ return;
+ detail::shutdown_socket(socket.sock);
+}
+
+inline void ClientImpl::close_socket(Socket &socket) {
+ // If there are requests in flight in another thread, usually closing
+ // the socket will be fine and they will simply receive an error when
+ // using the closed socket, but it is still a bug since rarely the OS
+ // may reassign the socket id to be used for a new socket, and then
+ // suddenly they will be operating on a live socket that is different
+ // than the one they intended!
+ assert(socket_requests_in_flight_ == 0 ||
+ socket_requests_are_from_thread_ == std::this_thread::get_id());
+ // It is also a bug if this happens while SSL is still active
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ assert(socket.ssl == nullptr);
+#endif
+ if (socket.sock == INVALID_SOCKET)
+ return;
+ detail::close_socket(socket.sock);
+ socket.sock = INVALID_SOCKET;
}
-inline bool Client::read_response_line(Stream &strm, Response &res) {
+inline void ClientImpl::lock_socket_and_shutdown_and_close() {
+ std::lock_guard<std::mutex> guard(socket_mutex_);
+ shutdown_ssl(socket_, true);
+ shutdown_socket(socket_);
+ close_socket(socket_);
+}
+
+inline bool ClientImpl::read_response_line(Stream &strm, Response &res) {
std::array<char, 2048> buf;
detail::stream_line_reader line_reader(strm, buf.data(), buf.size());
if (!line_reader.getline()) { return false; }
- const static std::regex re("(HTTP/1\\.[01]) (\\d+?) .*\r\n");
+ const static std::regex re("(HTTP/1\\.[01]) (\\d+) (.*?)\r\n");
std::cmatch m;
- if (std::regex_match(line_reader.ptr(), m, re)) {
+ if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
+ res.version = std::string(m[1]);
+ res.status = std::stoi(std::string(m[2]));
+ res.reason = std::string(m[3]);
+
+ // Ignore '100 Continue'
+ while (res.status == 100) {
+ if (!line_reader.getline()) { return false; } // CRLF
+ if (!line_reader.getline()) { return false; } // next response line
+
+ if (!std::regex_match(line_reader.ptr(), m, re)) { return false; }
res.version = std::string(m[1]);
res.status = std::stoi(std::string(m[2]));
+ res.reason = std::string(m[3]);
}
return true;
}
-inline bool Client::send(const Request &req, Response &res) {
- auto sock = create_client_socket();
- if (sock == INVALID_SOCKET) { return false; }
+inline bool ClientImpl::send(const Request &req, Response &res) {
+ std::lock_guard<std::recursive_mutex> request_mutex_guard(request_mutex_);
+
+ {
+ std::lock_guard<std::mutex> guard(socket_mutex_);
+ // Set this to false immediately - if it ever gets set to true by the end of the
+ // request, we know another thread instructed us to close the socket.
+ socket_should_be_closed_when_request_is_done_ = false;
+
+ auto is_alive = false;
+ if (socket_.is_open()) {
+ is_alive = detail::select_write(socket_.sock, 0, 0) > 0;
+ if (!is_alive) {
+ // Attempt to avoid sigpipe by shutting down nongracefully if it seems like
+ // the other side has already closed the connection
+ // Also, there cannot be any requests in flight from other threads since we locked
+ // request_mutex_, so safe to close everything immediately
+ const bool shutdown_gracefully = false;
+ shutdown_ssl(socket_, shutdown_gracefully);
+ shutdown_socket(socket_);
+ close_socket(socket_);
+ }
+ }
+
+ if (!is_alive) {
+ if (!create_and_connect_socket(socket_)) { return false; }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- if (is_ssl() && !proxy_host_.empty()) {
- bool error;
- if (!connect(sock, res, error)) { return error; }
- }
+ // TODO: refactoring
+ if (is_ssl()) {
+ auto &scli = static_cast<SSLClient &>(*this);
+ if (!proxy_host_.empty() && proxy_port_ != -1) {
+ bool success = false;
+ if (!scli.connect_with_proxy(socket_, res, success)) {
+ return success;
+ }
+ }
+
+ if (!scli.initialize_ssl(socket_)) { return false; }
+ }
#endif
+ }
- return process_and_close_socket(
- sock, 1, [&](Stream &strm, bool last_connection, bool &connection_close) {
- return handle_request(strm, req, res, last_connection,
- connection_close);
- });
-}
+ // Mark the current socket as being in use so that it cannot be closed by anyone
+ // else while this request is ongoing, even though we will be releasing the mutex.
+ if (socket_requests_in_flight_ > 1) {
+ assert(socket_requests_are_from_thread_ == std::this_thread::get_id());
+ }
+ socket_requests_in_flight_ += 1;
+ socket_requests_are_from_thread_ = std::this_thread::get_id();
+ }
-inline bool Client::send(const std::vector<Request> &requests,
- std::vector<Response> &responses) {
- size_t i = 0;
- while (i < requests.size()) {
- auto sock = create_client_socket();
- if (sock == INVALID_SOCKET) { return false; }
+ auto close_connection = !keep_alive_;
+ auto ret = process_socket(socket_, [&](Stream &strm) {
+ return handle_request(strm, req, res, close_connection);
+ });
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- if (is_ssl() && !proxy_host_.empty()) {
- Response res;
- bool error;
- if (!connect(sock, res, error)) { return false; }
+ //Briefly lock mutex in order to mark that a request is no longer ongoing
+ {
+ std::lock_guard<std::mutex> guard(socket_mutex_);
+ socket_requests_in_flight_ -= 1;
+ if (socket_requests_in_flight_ <= 0) {
+ assert(socket_requests_in_flight_ == 0);
+ socket_requests_are_from_thread_ = std::thread::id();
}
-#endif
- if (!process_and_close_socket(sock, requests.size() - i,
- [&](Stream &strm, bool last_connection,
- bool &connection_close) -> bool {
- auto &req = requests[i++];
- auto res = Response();
- auto ret = handle_request(strm, req, res,
- last_connection,
- connection_close);
- if (ret) {
- responses.emplace_back(std::move(res));
- }
- return ret;
- })) {
- return false;
+ if (socket_should_be_closed_when_request_is_done_ ||
+ close_connection ||
+ !ret ) {
+ shutdown_ssl(socket_, true);
+ shutdown_socket(socket_);
+ close_socket(socket_);
}
}
- return true;
+ if (!ret) {
+ if (error_ == Error::Success) { error_ = Error::Unknown; }
+ }
+
+ return ret;
}
-inline bool Client::handle_request(Stream &strm, const Request &req,
- Response &res, bool last_connection,
- bool &connection_close) {
- if (req.path.empty()) { return false; }
+inline bool ClientImpl::handle_request(Stream &strm, const Request &req,
+ Response &res, bool close_connection) {
+ if (req.path.empty()) {
+ error_ = Error::Connection;
+ return false;
+ }
bool ret;
- if (!is_ssl() && !proxy_host_.empty()) {
+ if (!is_ssl() && !proxy_host_.empty() && proxy_port_ != -1) {
auto req2 = req;
req2.path = "http://" + host_and_port_ + req.path;
- ret = process_request(strm, req2, res, last_connection, connection_close);
+ ret = process_request(strm, req2, res, close_connection);
} else {
- ret = process_request(strm, req, res, last_connection, connection_close);
+ ret = process_request(strm, req, res, close_connection);
}
if (!ret) { return false; }
@@ -3656,7 +4914,8 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
}
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- if (res.status == 401 || res.status == 407) {
+ if ((res.status == 401 || res.status == 407) &&
+ req.authorization_count_ < 5) {
auto is_proxy = res.status == 407;
const auto &username =
is_proxy ? proxy_digest_auth_username_ : digest_auth_username_;
@@ -3665,12 +4924,14 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
if (!username.empty() && !password.empty()) {
std::map<std::string, std::string> auth;
- if (parse_www_authenticate(res, auth, is_proxy)) {
+ if (detail::parse_www_authenticate(res, auth, is_proxy)) {
Request new_req = req;
- auto key = is_proxy ? "Proxy-Authorization" : "WWW-Authorization";
+ new_req.authorization_count_ += 1;
+ auto key = is_proxy ? "Proxy-Authorization" : "Authorization";
new_req.headers.erase(key);
- new_req.headers.insert(make_digest_authentication_header(
- req, auth, 1, random_string(10), username, password, is_proxy));
+ new_req.headers.insert(detail::make_digest_authentication_header(
+ req, auth, new_req.authorization_count_, detail::random_string(10),
+ username, password, is_proxy));
Response new_res;
@@ -3684,102 +4945,64 @@ inline bool Client::handle_request(Stream &strm, const Request &req,
return ret;
}
-#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
-inline bool Client::connect(socket_t sock, Response &res, bool &error) {
- error = true;
- Response res2;
-
- if (!detail::process_socket(
- true, sock, 1, read_timeout_sec_, read_timeout_usec_,
- [&](Stream &strm, bool /*last_connection*/, bool &connection_close) {
- Request req2;
- req2.method = "CONNECT";
- req2.path = host_and_port_;
- return process_request(strm, req2, res2, false, connection_close);
- })) {
- detail::close_socket(sock);
- error = false;
+inline bool ClientImpl::redirect(const Request &req, Response &res) {
+ if (req.redirect_count == 0) {
+ error_ = Error::ExceedRedirectCount;
return false;
}
- if (res2.status == 407) {
- if (!proxy_digest_auth_username_.empty() &&
- !proxy_digest_auth_password_.empty()) {
- std::map<std::string, std::string> auth;
- if (parse_www_authenticate(res2, auth, true)) {
- Response res3;
- if (!detail::process_socket(
- true, sock, 1, read_timeout_sec_, read_timeout_usec_,
- [&](Stream &strm, bool /*last_connection*/,
- bool &connection_close) {
- Request req3;
- req3.method = "CONNECT";
- req3.path = host_and_port_;
- req3.headers.insert(make_digest_authentication_header(
- req3, auth, 1, random_string(10),
- proxy_digest_auth_username_, proxy_digest_auth_password_,
- true));
- return process_request(strm, req3, res3, false,
- connection_close);
- })) {
- detail::close_socket(sock);
- error = false;
- return false;
- }
- }
- } else {
- res = res2;
- return false;
- }
- }
-
- return true;
-}
-#endif
-
-inline bool Client::redirect(const Request &req, Response &res) {
- if (req.redirect_count == 0) { return false; }
-
- auto location = res.get_header_value("location");
+ auto location = detail::decode_url(res.get_header_value("location"), true);
if (location.empty()) { return false; }
const static std::regex re(
- R"(^(?:([^:/?#]+):)?(?://([^/?#]*))?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
+ R"(^(?:(https?):)?(?://([^:/?#]*)(?::(\d+))?)?([^?#]*(?:\?[^#]*)?)(?:#.*)?)");
std::smatch m;
- if (!regex_match(location, m, re)) { return false; }
+ if (!std::regex_match(location, m, re)) { return false; }
auto scheme = is_ssl() ? "https" : "http";
auto next_scheme = m[1].str();
auto next_host = m[2].str();
- auto next_path = m[3].str();
- if (next_scheme.empty()) { next_scheme = scheme; }
+ auto port_str = m[3].str();
+ auto next_path = m[4].str();
+
+ auto next_port = port_;
+ if (!port_str.empty()) {
+ next_port = std::stoi(port_str);
+ } else if (!next_scheme.empty()) {
+ next_port = next_scheme == "https" ? 443 : 80;
+ }
+
if (next_scheme.empty()) { next_scheme = scheme; }
if (next_host.empty()) { next_host = host_; }
if (next_path.empty()) { next_path = "/"; }
- if (next_scheme == scheme && next_host == host_) {
+ if (next_scheme == scheme && next_host == host_ && next_port == port_) {
return detail::redirect(*this, req, res, next_path);
} else {
if (next_scheme == "https") {
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
- SSLClient cli(next_host.c_str());
+ SSLClient cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
- return detail::redirect(cli, req, res, next_path);
+ auto ret = detail::redirect(cli, req, res, next_path);
+ if (!ret) { error_ = cli.get_last_error(); }
+ return ret;
#else
return false;
#endif
} else {
- Client cli(next_host.c_str());
+ ClientImpl cli(next_host.c_str(), next_port);
cli.copy_settings(*this);
- return detail::redirect(cli, req, res, next_path);
+ auto ret = detail::redirect(cli, req, res, next_path);
+ if (!ret) { error_ = cli.get_last_error(); }
+ return ret;
}
}
}
-inline bool Client::write_request(Stream &strm, const Request &req,
- bool last_connection) {
+inline bool ClientImpl::write_request(Stream &strm, const Request &req,
+ bool close_connection) {
detail::BufferStream bstrm;
// Request line
@@ -3789,7 +5012,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
// Additonal headers
Headers headers;
- if (last_connection) { headers.emplace("Connection", "close"); }
+ if (close_connection) { headers.emplace("Connection", "close"); }
if (!req.has_header("Host")) {
if (is_ssl()) {
@@ -3810,7 +5033,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
if (!req.has_header("Accept")) { headers.emplace("Accept", "*/*"); }
if (!req.has_header("User-Agent")) {
- headers.emplace("User-Agent", "cpp-httplib/0.5");
+ headers.emplace("User-Agent", "cpp-httplib/0.7");
}
if (req.body.empty()) {
@@ -3818,7 +5041,10 @@ inline bool Client::write_request(Stream &strm, const Request &req,
auto length = std::to_string(req.content_length);
headers.emplace("Content-Length", length);
} else {
- headers.emplace("Content-Length", "0");
+ if (req.method == "POST" || req.method == "PUT" ||
+ req.method == "PATCH") {
+ headers.emplace("Content-Length", "0");
+ }
}
} else {
if (!req.has_header("Content-Type")) {
@@ -3831,7 +5057,7 @@ inline bool Client::write_request(Stream &strm, const Request &req,
}
}
- if (!basic_auth_username_.empty() && !basic_auth_password_.empty()) {
+ if (!basic_auth_password_.empty()) {
headers.insert(make_basic_authentication_header(
basic_auth_username_, basic_auth_password_, false));
}
@@ -3842,11 +5068,24 @@ inline bool Client::write_request(Stream &strm, const Request &req,
proxy_basic_auth_username_, proxy_basic_auth_password_, true));
}
+ if (!bearer_token_auth_token_.empty()) {
+ headers.insert(make_bearer_token_authentication_header(
+ bearer_token_auth_token_, false));
+ }
+
+ if (!proxy_bearer_token_auth_token_.empty()) {
+ headers.insert(make_bearer_token_authentication_header(
+ proxy_bearer_token_auth_token_, true));
+ }
+
detail::write_headers(bstrm, req, headers);
// Flush buffer
auto &data = bstrm.get_buffer();
- strm.write(data.data(), data.size());
+ if (!detail::write_data(strm, data.data(), data.size())) {
+ error_ = Error::Write;
+ return false;
+ }
// Body
if (req.body.empty()) {
@@ -3854,282 +5093,370 @@ inline bool Client::write_request(Stream &strm, const Request &req,
size_t offset = 0;
size_t end_offset = req.content_length;
+ bool ok = true;
+
DataSink data_sink;
data_sink.write = [&](const char *d, size_t l) {
- auto written_length = strm.write(d, l);
- offset += written_length;
+ if (ok) {
+ if (detail::write_data(strm, d, l)) {
+ offset += l;
+ } else {
+ ok = false;
+ }
+ }
};
- data_sink.is_writable = [&](void) { return strm.is_writable(); };
+ data_sink.is_writable = [&](void) { return ok && strm.is_writable(); };
while (offset < end_offset) {
- req.content_provider(offset, end_offset - offset, data_sink);
+ if (!req.content_provider(offset, end_offset - offset, data_sink)) {
+ error_ = Error::Canceled;
+ return false;
+ }
+ if (!ok) {
+ error_ = Error::Write;
+ return false;
+ }
}
}
} else {
- strm.write(req.body);
+ return detail::write_data(strm, req.body.data(), req.body.size());
}
return true;
}
-inline std::shared_ptr<Response> Client::send_with_content_provider(
+inline std::unique_ptr<Response> ClientImpl::send_with_content_provider(
const char *method, const char *path, const Headers &headers,
const std::string &body, size_t content_length,
ContentProvider content_provider, const char *content_type) {
+
Request req;
req.method = method;
- req.headers = headers;
+ req.headers = default_headers_;
+ req.headers.insert(headers.begin(), headers.end());
req.path = path;
- req.headers.emplace("Content-Type", content_type);
+ if (content_type) { req.headers.emplace("Content-Type", content_type); }
#ifdef CPPHTTPLIB_ZLIB_SUPPORT
if (compress_) {
+ detail::gzip_compressor compressor;
+
if (content_provider) {
+ auto ok = true;
size_t offset = 0;
DataSink data_sink;
data_sink.write = [&](const char *data, size_t data_len) {
- req.body.append(data, data_len);
- offset += data_len;
+ if (ok) {
+ auto last = offset + data_len == content_length;
+
+ auto ret = compressor.compress(
+ data, data_len, last, [&](const char *data, size_t data_len) {
+ req.body.append(data, data_len);
+ return true;
+ });
+
+ if (ret) {
+ offset += data_len;
+ } else {
+ ok = false;
+ }
+ }
};
- data_sink.is_writable = [&](void) { return true; };
+ data_sink.is_writable = [&](void) { return ok && true; };
- while (offset < content_length) {
- content_provider(offset, content_length - offset, data_sink);
+ while (ok && offset < content_length) {
+ if (!content_provider(offset, content_length - offset, data_sink)) {
+ error_ = Error::Canceled;
+ return nullptr;
+ }
}
} else {
- req.body = body;
+ if (!compressor.compress(body.data(), body.size(), true,
+ [&](const char *data, size_t data_len) {
+ req.body.append(data, data_len);
+ return true;
+ })) {
+ return nullptr;
+ }
}
- if (!detail::compress(req.body)) { return nullptr; }
req.headers.emplace("Content-Encoding", "gzip");
} else
#endif
{
if (content_provider) {
req.content_length = content_length;
- req.content_provider = content_provider;
+ req.content_provider = std::move(content_provider);
} else {
req.body = body;
}
}
- auto res = std::make_shared<Response>();
+ auto res = detail::make_unique<Response>();
- return send(req, *res) ? res : nullptr;
+ return send(req, *res) ? std::move(res) : nullptr;
}
-inline bool Client::process_request(Stream &strm, const Request &req,
- Response &res, bool last_connection,
- bool &connection_close) {
+inline bool ClientImpl::process_request(Stream &strm, const Request &req,
+ Response &res, bool close_connection) {
// Send request
- if (!write_request(strm, req, last_connection)) { return false; }
+ if (!write_request(strm, req, close_connection)) { return false; }
// Receive response and headers
if (!read_response_line(strm, res) ||
!detail::read_headers(strm, res.headers)) {
+ error_ = Error::Read;
return false;
}
- if (res.get_header_value("Connection") == "close" ||
- res.version == "HTTP/1.0") {
- connection_close = true;
- }
-
if (req.response_handler) {
- if (!req.response_handler(res)) { return false; }
+ if (!req.response_handler(res)) {
+ error_ = Error::Canceled;
+ return false;
+ }
}
// Body
if (req.method != "HEAD" && req.method != "CONNECT") {
- ContentReceiver out = [&](const char *buf, size_t n) {
- if (res.body.size() + n > res.body.max_size()) { return false; }
- res.body.append(buf, n);
- return true;
+ auto out =
+ req.content_receiver
+ ? static_cast<ContentReceiverWithProgress>(
+ [&](const char *buf, size_t n, uint64_t off, uint64_t len) {
+ auto ret = req.content_receiver(buf, n, off, len);
+ if (!ret) { error_ = Error::Canceled; }
+ return ret;
+ })
+ : static_cast<ContentReceiverWithProgress>(
+ [&](const char *buf, size_t n, uint64_t /*off*/,
+ uint64_t /*len*/) {
+ if (res.body.size() + n > res.body.max_size()) {
+ return false;
+ }
+ res.body.append(buf, n);
+ return true;
+ });
+
+ auto progress = [&](uint64_t current, uint64_t total) {
+ if (!req.progress) { return true; }
+ auto ret = req.progress(current, total);
+ if (!ret) { error_ = Error::Canceled; }
+ return ret;
};
- if (req.content_receiver) {
- out = [&](const char *buf, size_t n) {
- return req.content_receiver(buf, n);
- };
- }
-
int dummy_status;
- if (!detail::read_content(strm, res, std::numeric_limits<size_t>::max(),
- dummy_status, req.progress, out)) {
+ if (!detail::read_content(strm, res, (std::numeric_limits<size_t>::max)(),
+ dummy_status, std::move(progress), std::move(out),
+ decompress_)) {
+ if (error_ != Error::Canceled) { error_ = Error::Read; }
return false;
}
}
+ if (res.get_header_value("Connection") == "close" ||
+ (res.version == "HTTP/1.0" && res.reason != "Connection established")) {
+ // TODO this requires a not-entirely-obvious chain of calls to be correct
+ // for this to be safe. Maybe a code refactor (such as moving this out to
+ // the send function and getting rid of the recursiveness of the mutex)
+ // could make this more obvious.
+
+ // This is safe to call because process_request is only called by handle_request
+ // which is only called by send, which locks the request mutex during the process.
+ // It would be a bug to call it from a different thread since it's a thread-safety
+ // issue to do these things to the socket if another thread is using the socket.
+ lock_socket_and_shutdown_and_close();
+ }
+
// Log
if (logger_) { logger_(req, res); }
return true;
}
-inline bool Client::process_and_close_socket(
- socket_t sock, size_t request_count,
- std::function<bool(Stream &strm, bool last_connection,
- bool &connection_close)>
- callback) {
- request_count = std::min(request_count, keep_alive_max_count_);
- return detail::process_and_close_socket(true, sock, request_count,
- read_timeout_sec_, read_timeout_usec_,
- callback);
+inline bool
+ClientImpl::process_socket(const Socket &socket,
+ std::function<bool(Stream &strm)> callback) {
+ return detail::process_client_socket(
+ socket.sock, read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
+ write_timeout_usec_, std::move(callback));
}
-inline bool Client::is_ssl() const { return false; }
+inline bool ClientImpl::is_ssl() const { return false; }
-inline std::shared_ptr<Response> Client::Get(const char *path) {
+inline Result ClientImpl::Get(const char *path) {
return Get(path, Headers(), Progress());
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- Progress progress) {
+inline Result ClientImpl::Get(const char *path, Progress progress) {
return Get(path, Headers(), std::move(progress));
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- const Headers &headers) {
+inline Result ClientImpl::Get(const char *path, const Headers &headers) {
return Get(path, headers, Progress());
}
-inline std::shared_ptr<Response>
-Client::Get(const char *path, const Headers &headers, Progress progress) {
+inline Result ClientImpl::Get(const char *path, const Headers &headers,
+ Progress progress) {
Request req;
req.method = "GET";
req.path = path;
- req.headers = headers;
+ req.headers = default_headers_;
+ req.headers.insert(headers.begin(), headers.end());
req.progress = std::move(progress);
- auto res = std::make_shared<Response>();
- return send(req, *res) ? res : nullptr;
+ auto res = detail::make_unique<Response>();
+ auto ret = send(req, *res);
+ return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- ContentReceiver content_receiver) {
- return Get(path, Headers(), nullptr, std::move(content_receiver), Progress());
+inline Result ClientImpl::Get(const char *path,
+ ContentReceiver content_receiver) {
+ return Get(path, Headers(), nullptr, std::move(content_receiver), nullptr);
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- ContentReceiver content_receiver,
- Progress progress) {
+inline Result ClientImpl::Get(const char *path,
+ ContentReceiver content_receiver,
+ Progress progress) {
return Get(path, Headers(), nullptr, std::move(content_receiver),
std::move(progress));
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- const Headers &headers,
- ContentReceiver content_receiver) {
- return Get(path, headers, nullptr, std::move(content_receiver), Progress());
+inline Result ClientImpl::Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver) {
+ return Get(path, headers, nullptr, std::move(content_receiver), nullptr);
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- const Headers &headers,
- ContentReceiver content_receiver,
- Progress progress) {
+inline Result ClientImpl::Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver,
+ Progress progress) {
return Get(path, headers, nullptr, std::move(content_receiver),
std::move(progress));
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- const Headers &headers,
- ResponseHandler response_handler,
- ContentReceiver content_receiver) {
- return Get(path, headers, std::move(response_handler), content_receiver,
- Progress());
+inline Result ClientImpl::Get(const char *path,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver) {
+ return Get(path, Headers(), std::move(response_handler),
+ std::move(content_receiver), nullptr);
+}
+
+inline Result ClientImpl::Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver) {
+ return Get(path, headers, std::move(response_handler),
+ std::move(content_receiver), nullptr);
+}
+
+inline Result ClientImpl::Get(const char *path,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver,
+ Progress progress) {
+ return Get(path, Headers(), std::move(response_handler),
+ std::move(content_receiver), std::move(progress));
}
-inline std::shared_ptr<Response> Client::Get(const char *path,
- const Headers &headers,
- ResponseHandler response_handler,
- ContentReceiver content_receiver,
- Progress progress) {
+inline Result ClientImpl::Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver,
+ Progress progress) {
Request req;
req.method = "GET";
req.path = path;
- req.headers = headers;
+ req.headers = default_headers_;
+ req.headers.insert(headers.begin(), headers.end());
req.response_handler = std::move(response_handler);
- req.content_receiver = std::move(content_receiver);
+ req.content_receiver =
+ [content_receiver](const char *data, size_t data_length,
+ uint64_t /*offset*/, uint64_t /*total_length*/) {
+ return content_receiver(data, data_length);
+ };
req.progress = std::move(progress);
- auto res = std::make_shared<Response>();
- return send(req, *res) ? res : nullptr;
+ auto res = detail::make_unique<Response>();
+ auto ret = send(req, *res);
+ return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
-inline std::shared_ptr<Response> Client::Head(const char *path) {
+inline Result ClientImpl::Head(const char *path) {
return Head(path, Headers());
}
-inline std::shared_ptr<Response> Client::Head(const char *path,
- const Headers &headers) {
+inline Result ClientImpl::Head(const char *path, const Headers &headers) {
Request req;
req.method = "HEAD";
- req.headers = headers;
+ req.headers = default_headers_;
+ req.headers.insert(headers.begin(), headers.end());
req.path = path;
- auto res = std::make_shared<Response>();
+ auto res = detail::make_unique<Response>();
+ auto ret = send(req, *res);
+ return Result{ret ? std::move(res) : nullptr, get_last_error()};
+}
- return send(req, *res) ? res : nullptr;
+inline Result ClientImpl::Post(const char *path) {
+ return Post(path, std::string(), nullptr);
}
-inline std::shared_ptr<Response> Client::Post(const char *path,
- const std::string &body,
- const char *content_type) {
+inline Result ClientImpl::Post(const char *path, const std::string &body,
+ const char *content_type) {
return Post(path, Headers(), body, content_type);
}
-inline std::shared_ptr<Response> Client::Post(const char *path,
- const Headers &headers,
- const std::string &body,
- const char *content_type) {
- return send_with_content_provider("POST", path, headers, body, 0, nullptr,
- content_type);
+inline Result ClientImpl::Post(const char *path, const Headers &headers,
+ const std::string &body,
+ const char *content_type) {
+ auto ret = send_with_content_provider("POST", path, headers, body, 0, nullptr,
+ content_type);
+ return Result{std::move(ret), get_last_error()};
}
-inline std::shared_ptr<Response> Client::Post(const char *path,
- const Params &params) {
+inline Result ClientImpl::Post(const char *path, const Params &params) {
return Post(path, Headers(), params);
}
-inline std::shared_ptr<Response> Client::Post(const char *path,
- size_t content_length,
- ContentProvider content_provider,
- const char *content_type) {
- return Post(path, Headers(), content_length, content_provider, content_type);
+inline Result ClientImpl::Post(const char *path, size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return Post(path, Headers(), content_length, std::move(content_provider),
+ content_type);
}
-inline std::shared_ptr<Response>
-Client::Post(const char *path, const Headers &headers, size_t content_length,
- ContentProvider content_provider, const char *content_type) {
- return send_with_content_provider("POST", path, headers, std::string(),
- content_length, content_provider,
- content_type);
+inline Result ClientImpl::Post(const char *path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ auto ret = send_with_content_provider(
+ "POST", path, headers, std::string(), content_length,
+ std::move(content_provider), content_type);
+ return Result{std::move(ret), get_last_error()};
}
-inline std::shared_ptr<Response>
-Client::Post(const char *path, const Headers &headers, const Params &params) {
- std::string query;
- for (auto it = params.begin(); it != params.end(); ++it) {
- if (it != params.begin()) { query += "&"; }
- query += it->first;
- query += "=";
- query += detail::encode_url(it->second);
- }
-
+inline Result ClientImpl::Post(const char *path, const Headers &headers,
+ const Params &params) {
+ auto query = detail::params_to_query_str(params);
return Post(path, headers, query, "application/x-www-form-urlencoded");
}
-inline std::shared_ptr<Response>
-Client::Post(const char *path, const MultipartFormDataItems &items) {
+inline Result ClientImpl::Post(const char *path,
+ const MultipartFormDataItems &items) {
return Post(path, Headers(), items);
}
-inline std::shared_ptr<Response>
-Client::Post(const char *path, const Headers &headers,
- const MultipartFormDataItems &items) {
- auto boundary = detail::make_multipart_data_boundary();
+inline Result ClientImpl::Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items) {
+ return Post(path, headers, items, detail::make_multipart_data_boundary());
+}
+inline Result ClientImpl::Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items,
+ const std::string &boundary) {
+ for (size_t i = 0; i < boundary.size(); i++) {
+ char c = boundary[i];
+ if (!std::isalnum(c) && c != '-' && c != '_') {
+ error_ = Error::UnsupportedMultipartBoundaryChars;
+ return Result{nullptr, error_};
+ }
+ }
std::string body;
@@ -4153,182 +5480,240 @@ Client::Post(const char *path, const Headers &headers,
return Post(path, headers, body, content_type.c_str());
}
-inline std::shared_ptr<Response> Client::Put(const char *path,
- const std::string &body,
- const char *content_type) {
+inline Result ClientImpl::Put(const char *path) {
+ return Put(path, std::string(), nullptr);
+}
+
+inline Result ClientImpl::Put(const char *path, const std::string &body,
+ const char *content_type) {
return Put(path, Headers(), body, content_type);
}
-inline std::shared_ptr<Response> Client::Put(const char *path,
- const Headers &headers,
- const std::string &body,
- const char *content_type) {
- return send_with_content_provider("PUT", path, headers, body, 0, nullptr,
- content_type);
+inline Result ClientImpl::Put(const char *path, const Headers &headers,
+ const std::string &body,
+ const char *content_type) {
+ auto ret = send_with_content_provider("PUT", path, headers, body, 0, nullptr,
+ content_type);
+ return Result{std::move(ret), get_last_error()};
}
-inline std::shared_ptr<Response> Client::Put(const char *path,
- size_t content_length,
- ContentProvider content_provider,
- const char *content_type) {
- return Put(path, Headers(), content_length, content_provider, content_type);
+inline Result ClientImpl::Put(const char *path, size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return Put(path, Headers(), content_length, std::move(content_provider),
+ content_type);
}
-inline std::shared_ptr<Response>
-Client::Put(const char *path, const Headers &headers, size_t content_length,
- ContentProvider content_provider, const char *content_type) {
- return send_with_content_provider("PUT", path, headers, std::string(),
- content_length, content_provider,
- content_type);
+inline Result ClientImpl::Put(const char *path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ auto ret = send_with_content_provider(
+ "PUT", path, headers, std::string(), content_length,
+ std::move(content_provider), content_type);
+ return Result{std::move(ret), get_last_error()};
}
-inline std::shared_ptr<Response> Client::Put(const char *path,
- const Params &params) {
+inline Result ClientImpl::Put(const char *path, const Params &params) {
return Put(path, Headers(), params);
}
-inline std::shared_ptr<Response>
-Client::Put(const char *path, const Headers &headers, const Params &params) {
- std::string query;
- for (auto it = params.begin(); it != params.end(); ++it) {
- if (it != params.begin()) { query += "&"; }
- query += it->first;
- query += "=";
- query += detail::encode_url(it->second);
- }
-
+inline Result ClientImpl::Put(const char *path, const Headers &headers,
+ const Params &params) {
+ auto query = detail::params_to_query_str(params);
return Put(path, headers, query, "application/x-www-form-urlencoded");
}
-inline std::shared_ptr<Response> Client::Patch(const char *path,
- const std::string &body,
- const char *content_type) {
+inline Result ClientImpl::Patch(const char *path, const std::string &body,
+ const char *content_type) {
return Patch(path, Headers(), body, content_type);
}
-inline std::shared_ptr<Response> Client::Patch(const char *path,
- const Headers &headers,
- const std::string &body,
- const char *content_type) {
- return send_with_content_provider("PATCH", path, headers, body, 0, nullptr,
- content_type);
+inline Result ClientImpl::Patch(const char *path, const Headers &headers,
+ const std::string &body,
+ const char *content_type) {
+ auto ret = send_with_content_provider("PATCH", path, headers, body, 0,
+ nullptr, content_type);
+ return Result{std::move(ret), get_last_error()};
}
-inline std::shared_ptr<Response> Client::Patch(const char *path,
- size_t content_length,
- ContentProvider content_provider,
- const char *content_type) {
- return Patch(path, Headers(), content_length, content_provider, content_type);
+inline Result ClientImpl::Patch(const char *path, size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return Patch(path, Headers(), content_length, std::move(content_provider),
+ content_type);
}
-inline std::shared_ptr<Response>
-Client::Patch(const char *path, const Headers &headers, size_t content_length,
- ContentProvider content_provider, const char *content_type) {
- return send_with_content_provider("PATCH", path, headers, std::string(),
- content_length, content_provider,
- content_type);
+inline Result ClientImpl::Patch(const char *path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ auto ret = send_with_content_provider(
+ "PATCH", path, headers, std::string(), content_length,
+ std::move(content_provider), content_type);
+ return Result{std::move(ret), get_last_error()};
}
-inline std::shared_ptr<Response> Client::Delete(const char *path) {
+inline Result ClientImpl::Delete(const char *path) {
return Delete(path, Headers(), std::string(), nullptr);
}
-inline std::shared_ptr<Response> Client::Delete(const char *path,
- const std::string &body,
- const char *content_type) {
+inline Result ClientImpl::Delete(const char *path, const std::string &body,
+ const char *content_type) {
return Delete(path, Headers(), body, content_type);
}
-inline std::shared_ptr<Response> Client::Delete(const char *path,
- const Headers &headers) {
+inline Result ClientImpl::Delete(const char *path, const Headers &headers) {
return Delete(path, headers, std::string(), nullptr);
}
-inline std::shared_ptr<Response> Client::Delete(const char *path,
- const Headers &headers,
- const std::string &body,
- const char *content_type) {
+inline Result ClientImpl::Delete(const char *path, const Headers &headers,
+ const std::string &body,
+ const char *content_type) {
Request req;
req.method = "DELETE";
- req.headers = headers;
+ req.headers = default_headers_;
+ req.headers.insert(headers.begin(), headers.end());
req.path = path;
if (content_type) { req.headers.emplace("Content-Type", content_type); }
req.body = body;
- auto res = std::make_shared<Response>();
-
- return send(req, *res) ? res : nullptr;
+ auto res = detail::make_unique<Response>();
+ auto ret = send(req, *res);
+ return Result{ret ? std::move(res) : nullptr, get_last_error()};
}
-inline std::shared_ptr<Response> Client::Options(const char *path) {
+inline Result ClientImpl::Options(const char *path) {
return Options(path, Headers());
}
-inline std::shared_ptr<Response> Client::Options(const char *path,
- const Headers &headers) {
+inline Result ClientImpl::Options(const char *path, const Headers &headers) {
Request req;
req.method = "OPTIONS";
+ req.headers = default_headers_;
+ req.headers.insert(headers.begin(), headers.end());
req.path = path;
- req.headers = headers;
- auto res = std::make_shared<Response>();
+ auto res = detail::make_unique<Response>();
+ auto ret = send(req, *res);
+ return Result{ret ? std::move(res) : nullptr, get_last_error()};
+}
- return send(req, *res) ? res : nullptr;
+inline size_t ClientImpl::is_socket_open() const {
+ std::lock_guard<std::mutex> guard(socket_mutex_);
+ return socket_.is_open();
}
-inline void Client::set_timeout_sec(time_t timeout_sec) {
- timeout_sec_ = timeout_sec;
+inline void ClientImpl::stop() {
+ std::lock_guard<std::mutex> guard(socket_mutex_);
+ // There is no guarantee that this doesn't get overwritten later, but set it so that
+ // there is a good chance that any threads stopping as a result pick up this error.
+ error_ = Error::Canceled;
+
+ // If there is anything ongoing right now, the ONLY thread-safe thing we can do
+ // is to shutdown_socket, so that threads using this socket suddenly discover
+ // they can't read/write any more and error out.
+ // Everything else (closing the socket, shutting ssl down) is unsafe because these
+ // actions are not thread-safe.
+ if (socket_requests_in_flight_ > 0) {
+ shutdown_socket(socket_);
+ // Aside from that, we set a flag for the socket to be closed when we're done.
+ socket_should_be_closed_when_request_is_done_ = true;
+ return;
+ }
+
+ //Otherwise, sitll holding the mutex, we can shut everything down ourselves
+ shutdown_ssl(socket_, true);
+ shutdown_socket(socket_);
+ close_socket(socket_);
}
-inline void Client::set_read_timeout(time_t sec, time_t usec) {
+inline void ClientImpl::set_connection_timeout(time_t sec, time_t usec) {
+ connection_timeout_sec_ = sec;
+ connection_timeout_usec_ = usec;
+}
+
+inline void ClientImpl::set_read_timeout(time_t sec, time_t usec) {
read_timeout_sec_ = sec;
read_timeout_usec_ = usec;
}
-inline void Client::set_keep_alive_max_count(size_t count) {
- keep_alive_max_count_ = count;
+inline void ClientImpl::set_write_timeout(time_t sec, time_t usec) {
+ write_timeout_sec_ = sec;
+ write_timeout_usec_ = usec;
}
-inline void Client::set_basic_auth(const char *username, const char *password) {
+inline void ClientImpl::set_basic_auth(const char *username,
+ const char *password) {
basic_auth_username_ = username;
basic_auth_password_ = password;
}
+inline void ClientImpl::set_bearer_token_auth(const char *token) {
+ bearer_token_auth_token_ = token;
+}
+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
-inline void Client::set_digest_auth(const char *username,
- const char *password) {
+inline void ClientImpl::set_digest_auth(const char *username,
+ const char *password) {
digest_auth_username_ = username;
digest_auth_password_ = password;
}
#endif
-inline void Client::set_follow_location(bool on) { follow_location_ = on; }
+inline void ClientImpl::set_keep_alive(bool on) { keep_alive_ = on; }
-inline void Client::set_compress(bool on) { compress_ = on; }
+inline void ClientImpl::set_follow_location(bool on) { follow_location_ = on; }
-inline void Client::set_interface(const char *intf) { interface_ = intf; }
+inline void ClientImpl::set_default_headers(Headers headers) {
+ default_headers_ = std::move(headers);
+}
-inline void Client::set_proxy(const char *host, int port) {
+inline void ClientImpl::set_tcp_nodelay(bool on) { tcp_nodelay_ = on; }
+
+inline void ClientImpl::set_socket_options(SocketOptions socket_options) {
+ socket_options_ = std::move(socket_options);
+}
+
+inline void ClientImpl::set_compress(bool on) { compress_ = on; }
+
+inline void ClientImpl::set_decompress(bool on) { decompress_ = on; }
+
+inline void ClientImpl::set_interface(const char *intf) { interface_ = intf; }
+
+inline void ClientImpl::set_proxy(const char *host, int port) {
proxy_host_ = host;
proxy_port_ = port;
}
-inline void Client::set_proxy_basic_auth(const char *username,
- const char *password) {
+inline void ClientImpl::set_proxy_basic_auth(const char *username,
+ const char *password) {
proxy_basic_auth_username_ = username;
proxy_basic_auth_password_ = password;
}
+inline void ClientImpl::set_proxy_bearer_token_auth(const char *token) {
+ proxy_bearer_token_auth_token_ = token;
+}
+
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
-inline void Client::set_proxy_digest_auth(const char *username,
- const char *password) {
+inline void ClientImpl::set_proxy_digest_auth(const char *username,
+ const char *password) {
proxy_digest_auth_username_ = username;
proxy_digest_auth_password_ = password;
}
#endif
-inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); }
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+inline void ClientImpl::enable_server_certificate_verification(bool enabled) {
+ server_certificate_verification_ = enabled;
+}
+#endif
+
+inline void ClientImpl::set_logger(Logger logger) {
+ logger_ = std::move(logger);
+}
/*
* SSL Implementation
@@ -4336,72 +5721,69 @@ inline void Client::set_logger(Logger logger) { logger_ = std::move(logger); }
#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
namespace detail {
-template <typename U, typename V, typename T>
-inline bool process_and_close_socket_ssl(
- bool is_client_request, socket_t sock, size_t keep_alive_max_count,
- time_t read_timeout_sec, time_t read_timeout_usec, SSL_CTX *ctx,
- std::mutex &ctx_mutex, U SSL_connect_or_accept, V setup, T callback) {
- assert(keep_alive_max_count > 0);
-
+template <typename U, typename V>
+inline SSL *ssl_new(socket_t sock, SSL_CTX *ctx, std::mutex &ctx_mutex,
+ U SSL_connect_or_accept, V setup) {
SSL *ssl = nullptr;
{
std::lock_guard<std::mutex> guard(ctx_mutex);
ssl = SSL_new(ctx);
}
- if (!ssl) {
- close_socket(sock);
- return false;
- }
+ if (ssl) {
+ auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
+ SSL_set_bio(ssl, bio, bio);
- auto bio = BIO_new_socket(static_cast<int>(sock), BIO_NOCLOSE);
- SSL_set_bio(ssl, bio, bio);
-
- if (!setup(ssl)) {
- SSL_shutdown(ssl);
- {
- std::lock_guard<std::mutex> guard(ctx_mutex);
- SSL_free(ssl);
+ if (!setup(ssl) || SSL_connect_or_accept(ssl) != 1) {
+ SSL_shutdown(ssl);
+ {
+ std::lock_guard<std::mutex> guard(ctx_mutex);
+ SSL_free(ssl);
+ }
+ return nullptr;
}
-
- close_socket(sock);
- return false;
}
- auto ret = false;
+ return ssl;
+}
- if (SSL_connect_or_accept(ssl) == 1) {
- if (keep_alive_max_count > 1) {
- auto count = keep_alive_max_count;
- while (count > 0 &&
- (is_client_request ||
- detail::select_read(sock, CPPHTTPLIB_KEEPALIVE_TIMEOUT_SECOND,
- CPPHTTPLIB_KEEPALIVE_TIMEOUT_USECOND) > 0)) {
- SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
- auto last_connection = count == 1;
- auto connection_close = false;
-
- ret = callback(ssl, strm, last_connection, connection_close);
- if (!ret || connection_close) { break; }
-
- count--;
- }
- } else {
- SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec);
- auto dummy_connection_close = false;
- ret = callback(ssl, strm, true, dummy_connection_close);
- }
+inline void ssl_delete(std::mutex &ctx_mutex, SSL *ssl,
+ bool shutdown_gracefully) {
+ // sometimes we may want to skip this to try to avoid SIGPIPE if we know
+ // the remote has closed the network connection
+ // Note that it is not always possible to avoid SIGPIPE, this is merely a best-efforts.
+ if (shutdown_gracefully) {
+ SSL_shutdown(ssl);
}
- SSL_shutdown(ssl);
- {
- std::lock_guard<std::mutex> guard(ctx_mutex);
- SSL_free(ssl);
- }
+ std::lock_guard<std::mutex> guard(ctx_mutex);
+ SSL_free(ssl);
+}
- close_socket(sock);
+template <typename T>
+inline bool
+process_server_socket_ssl(SSL *ssl, socket_t sock, size_t keep_alive_max_count,
+ time_t keep_alive_timeout_sec,
+ time_t read_timeout_sec, time_t read_timeout_usec,
+ time_t write_timeout_sec, time_t write_timeout_usec,
+ T callback) {
+ return process_server_socket_core(
+ sock, keep_alive_max_count, keep_alive_timeout_sec,
+ [&](bool close_connection, bool &connection_closed) {
+ SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
+ write_timeout_sec, write_timeout_usec);
+ return callback(strm, close_connection, connection_closed);
+ });
+}
- return ret;
+template <typename T>
+inline bool
+process_client_socket_ssl(SSL *ssl, socket_t sock, time_t read_timeout_sec,
+ time_t read_timeout_usec, time_t write_timeout_sec,
+ time_t write_timeout_usec, T callback) {
+ SSLSocketStream strm(sock, ssl, read_timeout_sec, read_timeout_usec,
+ write_timeout_sec, write_timeout_usec);
+ return callback(strm);
}
#if OPENSSL_VERSION_NUMBER < 0x10100000L
@@ -4420,11 +5802,11 @@ public:
private:
static void locking_callback(int mode, int type, const char * /*file*/,
int /*line*/) {
- auto &locks = *openSSL_locks_;
+ auto &lk = (*openSSL_locks_)[static_cast<size_t>(type)];
if (mode & CRYPTO_LOCK) {
- locks[type].lock();
+ lk.lock();
} else {
- locks[type].unlock();
+ lk.unlock();
}
}
};
@@ -4458,9 +5840,15 @@ private:
// SSL socket stream implementation
inline SSLSocketStream::SSLSocketStream(socket_t sock, SSL *ssl,
time_t read_timeout_sec,
- time_t read_timeout_usec)
+ time_t read_timeout_usec,
+ time_t write_timeout_sec,
+ time_t write_timeout_usec)
: sock_(sock), ssl_(ssl), read_timeout_sec_(read_timeout_sec),
- read_timeout_usec_(read_timeout_usec) {}
+ read_timeout_usec_(read_timeout_usec),
+ write_timeout_sec_(write_timeout_sec),
+ write_timeout_usec_(write_timeout_usec) {
+ SSL_clear_mode(ssl, SSL_MODE_AUTO_RETRY);
+}
inline SSLSocketStream::~SSLSocketStream() {}
@@ -4469,24 +5857,44 @@ inline bool SSLSocketStream::is_readable() const {
}
inline bool SSLSocketStream::is_writable() const {
- return detail::select_write(sock_, 0, 0) > 0;
+ return detail::select_write(sock_, write_timeout_sec_, write_timeout_usec_) >
+ 0;
}
-inline int SSLSocketStream::read(char *ptr, size_t size) {
- if (SSL_pending(ssl_) > 0 ||
- select_read(sock_, read_timeout_sec_, read_timeout_usec_) > 0) {
+inline ssize_t SSLSocketStream::read(char *ptr, size_t size) {
+ if (SSL_pending(ssl_) > 0) {
return SSL_read(ssl_, ptr, static_cast<int>(size));
+ } else if (is_readable()) {
+ auto ret = SSL_read(ssl_, ptr, static_cast<int>(size));
+ if (ret < 0) {
+ auto err = SSL_get_error(ssl_, ret);
+ while (err == SSL_ERROR_WANT_READ) {
+ if (SSL_pending(ssl_) > 0) {
+ return SSL_read(ssl_, ptr, static_cast<int>(size));
+ } else if (is_readable()) {
+ ret = SSL_read(ssl_, ptr, static_cast<int>(size));
+ if (ret >= 0) {
+ return ret;
+ }
+ err = SSL_get_error(ssl_, ret);
+ } else {
+ return -1;
+ }
+ }
+ }
+ return ret;
}
return -1;
}
-inline int SSLSocketStream::write(const char *ptr, size_t size) {
+inline ssize_t SSLSocketStream::write(const char *ptr, size_t size) {
if (is_writable()) { return SSL_write(ssl_, ptr, static_cast<int>(size)); }
return -1;
}
-inline std::string SSLSocketStream::get_remote_addr() const {
- return detail::get_remote_addr(sock_);
+inline void SSLSocketStream::get_remote_ip_and_port(std::string &ip,
+ int &port) const {
+ detail::get_remote_ip_and_port(sock_, ip, port);
}
static SSLInit sslinit_;
@@ -4532,6 +5940,33 @@ inline SSLServer::SSLServer(const char *cert_path, const char *private_key_path,
}
}
+inline SSLServer::SSLServer(X509 *cert, EVP_PKEY *private_key,
+ X509_STORE *client_ca_cert_store) {
+ ctx_ = SSL_CTX_new(SSLv23_server_method());
+
+ if (ctx_) {
+ SSL_CTX_set_options(ctx_,
+ SSL_OP_ALL | SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3 |
+ SSL_OP_NO_COMPRESSION |
+ SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION);
+
+ if (SSL_CTX_use_certificate(ctx_, cert) != 1 ||
+ SSL_CTX_use_PrivateKey(ctx_, private_key) != 1) {
+ SSL_CTX_free(ctx_);
+ ctx_ = nullptr;
+ } else if (client_ca_cert_store) {
+
+ SSL_CTX_set_cert_store(ctx_, client_ca_cert_store);
+
+ SSL_CTX_set_verify(
+ ctx_,
+ SSL_VERIFY_PEER |
+ SSL_VERIFY_FAIL_IF_NO_PEER_CERT, // SSL_VERIFY_CLIENT_ONCE,
+ nullptr);
+ }
+ }
+}
+
inline SSLServer::~SSLServer() {
if (ctx_) { SSL_CTX_free(ctx_); }
}
@@ -4539,21 +5974,42 @@ inline SSLServer::~SSLServer() {
inline bool SSLServer::is_valid() const { return ctx_; }
inline bool SSLServer::process_and_close_socket(socket_t sock) {
- return detail::process_and_close_socket_ssl(
- false, sock, keep_alive_max_count_, read_timeout_sec_, read_timeout_usec_,
- ctx_, ctx_mutex_, SSL_accept, [](SSL * /*ssl*/) { return true; },
- [this](SSL *ssl, Stream &strm, bool last_connection,
- bool &connection_close) {
- return process_request(strm, last_connection, connection_close,
- [&](Request &req) { req.ssl = ssl; });
- });
+ auto ssl = detail::ssl_new(sock, ctx_, ctx_mutex_, SSL_accept,
+ [](SSL * /*ssl*/) { return true; });
+
+ if (ssl) {
+ auto ret = detail::process_server_socket_ssl(
+ ssl, sock, keep_alive_max_count_, keep_alive_timeout_sec_,
+ read_timeout_sec_, read_timeout_usec_, write_timeout_sec_,
+ write_timeout_usec_,
+ [this, ssl](Stream &strm, bool close_connection,
+ bool &connection_closed) {
+ return process_request(strm, close_connection, connection_closed,
+ [&](Request &req) { req.ssl = ssl; });
+ });
+
+ detail::ssl_delete(ctx_mutex_, ssl, ret);
+ detail::shutdown_socket(sock);
+ detail::close_socket(sock);
+ return ret;
+ }
+
+ detail::shutdown_socket(sock);
+ detail::close_socket(sock);
+ return false;
}
// SSL HTTP client implementation
+inline SSLClient::SSLClient(const std::string &host)
+ : SSLClient(host, 443, std::string(), std::string()) {}
+
+inline SSLClient::SSLClient(const std::string &host, int port)
+ : SSLClient(host, port, std::string(), std::string()) {}
+
inline SSLClient::SSLClient(const std::string &host, int port,
const std::string &client_cert_path,
const std::string &client_key_path)
- : Client(host, port, client_cert_path, client_key_path) {
+ : ClientImpl(host, port, client_cert_path, client_key_path) {
ctx_ = SSL_CTX_new(SSLv23_client_method());
detail::split(&host_[0], &host_[host_.size()], '.',
@@ -4571,8 +6027,30 @@ inline SSLClient::SSLClient(const std::string &host, int port,
}
}
+inline SSLClient::SSLClient(const std::string &host, int port,
+ X509 *client_cert, EVP_PKEY *client_key)
+ : ClientImpl(host, port) {
+ ctx_ = SSL_CTX_new(SSLv23_client_method());
+
+ detail::split(&host_[0], &host_[host_.size()], '.',
+ [&](const char *b, const char *e) {
+ host_components_.emplace_back(std::string(b, e));
+ });
+ if (client_cert != nullptr && client_key != nullptr) {
+ if (SSL_CTX_use_certificate(ctx_, client_cert) != 1 ||
+ SSL_CTX_use_PrivateKey(ctx_, client_key) != 1) {
+ SSL_CTX_free(ctx_);
+ ctx_ = nullptr;
+ }
+ }
+}
+
inline SSLClient::~SSLClient() {
if (ctx_) { SSL_CTX_free(ctx_); }
+ // Make sure to shut down SSL since shutdown_ssl will resolve to the
+ // base function rather than the derived function once we get to the
+ // base class destructor, and won't free the SSL (causing a leak).
+ SSLClient::shutdown_ssl(socket_, true);
}
inline bool SSLClient::is_valid() const { return ctx_; }
@@ -4583,67 +6061,188 @@ inline void SSLClient::set_ca_cert_path(const char *ca_cert_file_path,
if (ca_cert_dir_path) { ca_cert_dir_path_ = ca_cert_dir_path; }
}
-inline void SSLClient::enable_server_certificate_verification(bool enabled) {
- server_certificate_verification_ = enabled;
+inline void SSLClient::set_ca_cert_store(X509_STORE *ca_cert_store) {
+ if (ca_cert_store) {
+ if (ctx_) {
+ if (SSL_CTX_get_cert_store(ctx_) != ca_cert_store) {
+ // Free memory allocated for old cert and use new store `ca_cert_store`
+ SSL_CTX_set_cert_store(ctx_, ca_cert_store);
+ }
+ } else {
+ X509_STORE_free(ca_cert_store);
+ }
+ }
}
inline long SSLClient::get_openssl_verify_result() const {
return verify_result_;
}
-inline SSL_CTX *SSLClient::ssl_context() const noexcept { return ctx_; }
+inline SSL_CTX *SSLClient::ssl_context() const { return ctx_; }
-inline bool SSLClient::process_and_close_socket(
- socket_t sock, size_t request_count,
- std::function<bool(Stream &strm, bool last_connection,
- bool &connection_close)>
- callback) {
+inline bool SSLClient::create_and_connect_socket(Socket &socket) {
+ return is_valid() && ClientImpl::create_and_connect_socket(socket);
+}
- request_count = std::min(request_count, keep_alive_max_count_);
+// Assumes that socket_mutex_ is locked and that there are no requests in flight
+inline bool SSLClient::connect_with_proxy(Socket &socket, Response &res,
+ bool &success) {
+ success = true;
+ Response res2;
+ if (!detail::process_client_socket(
+ socket.sock, read_timeout_sec_, read_timeout_usec_,
+ write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
+ Request req2;
+ req2.method = "CONNECT";
+ req2.path = host_and_port_;
+ return process_request(strm, req2, res2, false);
+ })) {
+ // Thread-safe to close everything because we are assuming there are no requests in flight
+ shutdown_ssl(socket, true);
+ shutdown_socket(socket);
+ close_socket(socket);
+ success = false;
+ return false;
+ }
+
+ if (res2.status == 407) {
+ if (!proxy_digest_auth_username_.empty() &&
+ !proxy_digest_auth_password_.empty()) {
+ std::map<std::string, std::string> auth;
+ if (detail::parse_www_authenticate(res2, auth, true)) {
+ Response res3;
+ if (!detail::process_client_socket(
+ socket.sock, read_timeout_sec_, read_timeout_usec_,
+ write_timeout_sec_, write_timeout_usec_, [&](Stream &strm) {
+ Request req3;
+ req3.method = "CONNECT";
+ req3.path = host_and_port_;
+ req3.headers.insert(detail::make_digest_authentication_header(
+ req3, auth, 1, detail::random_string(10),
+ proxy_digest_auth_username_, proxy_digest_auth_password_,
+ true));
+ return process_request(strm, req3, res3, false);
+ })) {
+ // Thread-safe to close everything because we are assuming there are no requests in flight
+ shutdown_ssl(socket, true);
+ shutdown_socket(socket);
+ close_socket(socket);
+ success = false;
+ return false;
+ }
+ }
+ } else {
+ res = res2;
+ return false;
+ }
+ }
- return is_valid() &&
- detail::process_and_close_socket_ssl(
- true, sock, request_count, read_timeout_sec_, read_timeout_usec_,
- ctx_, ctx_mutex_,
- [&](SSL *ssl) {
- if (ca_cert_file_path_.empty()) {
- SSL_CTX_set_verify(ctx_, SSL_VERIFY_NONE, nullptr);
- } else {
- if (!SSL_CTX_load_verify_locations(
- ctx_, ca_cert_file_path_.c_str(), nullptr)) {
- return false;
- }
- SSL_CTX_set_verify(ctx_, SSL_VERIFY_PEER, nullptr);
- }
+ return true;
+}
- if (SSL_connect(ssl) != 1) { return false; }
+inline bool SSLClient::load_certs() {
+ bool ret = true;
- if (server_certificate_verification_) {
- verify_result_ = SSL_get_verify_result(ssl);
+ std::call_once(initialize_cert_, [&]() {
+ std::lock_guard<std::mutex> guard(ctx_mutex_);
+ if (!ca_cert_file_path_.empty()) {
+ if (!SSL_CTX_load_verify_locations(ctx_, ca_cert_file_path_.c_str(),
+ nullptr)) {
+ ret = false;
+ }
+ } else if (!ca_cert_dir_path_.empty()) {
+ if (!SSL_CTX_load_verify_locations(ctx_, nullptr,
+ ca_cert_dir_path_.c_str())) {
+ ret = false;
+ }
+ } else {
+#ifdef _WIN32
+ detail::load_system_certs_on_windows(SSL_CTX_get_cert_store(ctx_));
+#else
+ SSL_CTX_set_default_verify_paths(ctx_);
+#endif
+ }
+ });
- if (verify_result_ != X509_V_OK) { return false; }
+ return ret;
+}
- auto server_cert = SSL_get_peer_certificate(ssl);
+inline bool SSLClient::initialize_ssl(Socket &socket) {
+ auto ssl = detail::ssl_new(
+ socket.sock, ctx_, ctx_mutex_,
+ [&](SSL *ssl) {
+ if (server_certificate_verification_) {
+ if (!load_certs()) {
+ error_ = Error::SSLLoadingCerts;
+ return false;
+ }
+ SSL_set_verify(ssl, SSL_VERIFY_NONE, nullptr);
+ }
- if (server_cert == nullptr) { return false; }
+ if (SSL_connect(ssl) != 1) {
+ error_ = Error::SSLConnection;
+ return false;
+ }
- if (!verify_host(server_cert)) {
- X509_free(server_cert);
- return false;
- }
- X509_free(server_cert);
- }
+ if (server_certificate_verification_) {
+ verify_result_ = SSL_get_verify_result(ssl);
- return true;
- },
- [&](SSL *ssl) {
- SSL_set_tlsext_host_name(ssl, host_.c_str());
- return true;
- },
- [&](SSL * /*ssl*/, Stream &strm, bool last_connection,
- bool &connection_close) {
- return callback(strm, last_connection, connection_close);
- });
+ if (verify_result_ != X509_V_OK) {
+ error_ = Error::SSLServerVerification;
+ return false;
+ }
+
+ auto server_cert = SSL_get_peer_certificate(ssl);
+
+ if (server_cert == nullptr) {
+ error_ = Error::SSLServerVerification;
+ return false;
+ }
+
+ if (!verify_host(server_cert)) {
+ X509_free(server_cert);
+ error_ = Error::SSLServerVerification;
+ return false;
+ }
+ X509_free(server_cert);
+ }
+
+ return true;
+ },
+ [&](SSL *ssl) {
+ SSL_set_tlsext_host_name(ssl, host_.c_str());
+ return true;
+ });
+
+ if (ssl) {
+ socket.ssl = ssl;
+ return true;
+ }
+
+ shutdown_socket(socket);
+ close_socket(socket);
+ return false;
+}
+
+inline void SSLClient::shutdown_ssl(Socket &socket, bool shutdown_gracefully) {
+ if (socket.sock == INVALID_SOCKET) {
+ assert(socket.ssl == nullptr);
+ return;
+ }
+ if (socket.ssl) {
+ detail::ssl_delete(ctx_mutex_, socket.ssl, shutdown_gracefully);
+ socket.ssl = nullptr;
+ }
+ assert(socket.ssl == nullptr);
+}
+
+inline bool
+SSLClient::process_socket(const Socket &socket,
+ std::function<bool(Stream &strm)> callback) {
+ assert(socket.ssl);
+ return detail::process_client_socket_ssl(
+ socket.ssl, socket.sock, read_timeout_sec_, read_timeout_usec_,
+ write_timeout_sec_, write_timeout_usec_, std::move(callback));
}
inline bool SSLClient::is_ssl() const { return true; }
@@ -4703,7 +6302,7 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
auto count = sk_GENERAL_NAME_num(alt_names);
- for (auto i = 0; i < count && !dsn_matched; i++) {
+ for (decltype(count) i = 0; i < count && !dsn_matched; i++) {
auto val = sk_GENERAL_NAME_value(alt_names, i);
if (val->type == type) {
auto name = (const char *)ASN1_STRING_get0_data(val->d.ia5);
@@ -4728,7 +6327,6 @@ SSLClient::verify_host_with_subject_alt_name(X509 *server_cert) const {
}
GENERAL_NAMES_free((STACK_OF(GENERAL_NAME) *)alt_names);
-
return ret;
}
@@ -4740,7 +6338,9 @@ inline bool SSLClient::verify_host_with_common_name(X509 *server_cert) const {
auto name_len = X509_NAME_get_text_by_NID(subject_name, NID_commonName,
name, sizeof(name));
- if (name_len != -1) { return check_host_name(name, name_len); }
+ if (name_len != -1) {
+ return check_host_name(name, static_cast<size_t>(name_len));
+ }
}
return false;
@@ -4775,6 +6375,338 @@ inline bool SSLClient::check_host_name(const char *pattern,
}
#endif
+// Universal client implementation
+inline Client::Client(const char *scheme_host_port)
+ : Client(scheme_host_port, std::string(), std::string()) {}
+
+inline Client::Client(const char *scheme_host_port,
+ const std::string &client_cert_path,
+ const std::string &client_key_path) {
+ const static std::regex re(R"(^(?:([a-z]+)://)?([^:/?#]+)(?::(\d+))?)");
+
+ std::cmatch m;
+ if (std::regex_match(scheme_host_port, m, re)) {
+ auto scheme = m[1].str();
+
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ if (!scheme.empty() && (scheme != "http" && scheme != "https")) {
+#else
+ if (!scheme.empty() && scheme != "http") {
+#endif
+ std::string msg = "'" + scheme + "' scheme is not supported.";
+ throw std::invalid_argument(msg);
+ return;
+ }
+
+ auto is_ssl = scheme == "https";
+
+ auto host = m[2].str();
+
+ auto port_str = m[3].str();
+ auto port = !port_str.empty() ? std::stoi(port_str) : (is_ssl ? 443 : 80);
+
+ if (is_ssl) {
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+ cli_ = detail::make_unique<SSLClient>(host.c_str(), port,
+ client_cert_path, client_key_path);
+ is_ssl_ = is_ssl;
+#endif
+ } else {
+ cli_ = detail::make_unique<ClientImpl>(host.c_str(), port,
+ client_cert_path, client_key_path);
+ }
+ } else {
+ cli_ = detail::make_unique<ClientImpl>(scheme_host_port, 80,
+ client_cert_path, client_key_path);
+ }
+}
+
+inline Client::Client(const std::string &host, int port)
+ : cli_(detail::make_unique<ClientImpl>(host, port)) {}
+
+inline Client::Client(const std::string &host, int port,
+ const std::string &client_cert_path,
+ const std::string &client_key_path)
+ : cli_(detail::make_unique<ClientImpl>(host, port, client_cert_path,
+ client_key_path)) {}
+
+inline Client::~Client() {}
+
+inline bool Client::is_valid() const {
+ return cli_ != nullptr && cli_->is_valid();
+}
+
+inline Result Client::Get(const char *path) { return cli_->Get(path); }
+inline Result Client::Get(const char *path, const Headers &headers) {
+ return cli_->Get(path, headers);
+}
+inline Result Client::Get(const char *path, Progress progress) {
+ return cli_->Get(path, std::move(progress));
+}
+inline Result Client::Get(const char *path, const Headers &headers,
+ Progress progress) {
+ return cli_->Get(path, headers, std::move(progress));
+}
+inline Result Client::Get(const char *path, ContentReceiver content_receiver) {
+ return cli_->Get(path, std::move(content_receiver));
+}
+inline Result Client::Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver) {
+ return cli_->Get(path, headers, std::move(content_receiver));
+}
+inline Result Client::Get(const char *path, ContentReceiver content_receiver,
+ Progress progress) {
+ return cli_->Get(path, std::move(content_receiver), std::move(progress));
+}
+inline Result Client::Get(const char *path, const Headers &headers,
+ ContentReceiver content_receiver, Progress progress) {
+ return cli_->Get(path, headers, std::move(content_receiver),
+ std::move(progress));
+}
+inline Result Client::Get(const char *path, ResponseHandler response_handler,
+ ContentReceiver content_receiver) {
+ return cli_->Get(path, std::move(response_handler),
+ std::move(content_receiver));
+}
+inline Result Client::Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver) {
+ return cli_->Get(path, headers, std::move(response_handler),
+ std::move(content_receiver));
+}
+inline Result Client::Get(const char *path, ResponseHandler response_handler,
+ ContentReceiver content_receiver, Progress progress) {
+ return cli_->Get(path, std::move(response_handler),
+ std::move(content_receiver), std::move(progress));
+}
+inline Result Client::Get(const char *path, const Headers &headers,
+ ResponseHandler response_handler,
+ ContentReceiver content_receiver, Progress progress) {
+ return cli_->Get(path, headers, std::move(response_handler),
+ std::move(content_receiver), std::move(progress));
+}
+
+inline Result Client::Head(const char *path) { return cli_->Head(path); }
+inline Result Client::Head(const char *path, const Headers &headers) {
+ return cli_->Head(path, headers);
+}
+
+inline Result Client::Post(const char *path) { return cli_->Post(path); }
+inline Result Client::Post(const char *path, const std::string &body,
+ const char *content_type) {
+ return cli_->Post(path, body, content_type);
+}
+inline Result Client::Post(const char *path, const Headers &headers,
+ const std::string &body, const char *content_type) {
+ return cli_->Post(path, headers, body, content_type);
+}
+inline Result Client::Post(const char *path, size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return cli_->Post(path, content_length, std::move(content_provider),
+ content_type);
+}
+inline Result Client::Post(const char *path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return cli_->Post(path, headers, content_length, std::move(content_provider),
+ content_type);
+}
+inline Result Client::Post(const char *path, const Params &params) {
+ return cli_->Post(path, params);
+}
+inline Result Client::Post(const char *path, const Headers &headers,
+ const Params &params) {
+ return cli_->Post(path, headers, params);
+}
+inline Result Client::Post(const char *path,
+ const MultipartFormDataItems &items) {
+ return cli_->Post(path, items);
+}
+inline Result Client::Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items) {
+ return cli_->Post(path, headers, items);
+}
+inline Result Client::Post(const char *path, const Headers &headers,
+ const MultipartFormDataItems &items,
+ const std::string &boundary) {
+ return cli_->Post(path, headers, items, boundary);
+}
+inline Result Client::Put(const char *path) { return cli_->Put(path); }
+inline Result Client::Put(const char *path, const std::string &body,
+ const char *content_type) {
+ return cli_->Put(path, body, content_type);
+}
+inline Result Client::Put(const char *path, const Headers &headers,
+ const std::string &body, const char *content_type) {
+ return cli_->Put(path, headers, body, content_type);
+}
+inline Result Client::Put(const char *path, size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return cli_->Put(path, content_length, std::move(content_provider),
+ content_type);
+}
+inline Result Client::Put(const char *path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return cli_->Put(path, headers, content_length, std::move(content_provider),
+ content_type);
+}
+inline Result Client::Put(const char *path, const Params &params) {
+ return cli_->Put(path, params);
+}
+inline Result Client::Put(const char *path, const Headers &headers,
+ const Params &params) {
+ return cli_->Put(path, headers, params);
+}
+inline Result Client::Patch(const char *path, const std::string &body,
+ const char *content_type) {
+ return cli_->Patch(path, body, content_type);
+}
+inline Result Client::Patch(const char *path, const Headers &headers,
+ const std::string &body, const char *content_type) {
+ return cli_->Patch(path, headers, body, content_type);
+}
+inline Result Client::Patch(const char *path, size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return cli_->Patch(path, content_length, std::move(content_provider),
+ content_type);
+}
+inline Result Client::Patch(const char *path, const Headers &headers,
+ size_t content_length,
+ ContentProvider content_provider,
+ const char *content_type) {
+ return cli_->Patch(path, headers, content_length, std::move(content_provider),
+ content_type);
+}
+inline Result Client::Delete(const char *path) { return cli_->Delete(path); }
+inline Result Client::Delete(const char *path, const std::string &body,
+ const char *content_type) {
+ return cli_->Delete(path, body, content_type);
+}
+inline Result Client::Delete(const char *path, const Headers &headers) {
+ return cli_->Delete(path, headers);
+}
+inline Result Client::Delete(const char *path, const Headers &headers,
+ const std::string &body,
+ const char *content_type) {
+ return cli_->Delete(path, headers, body, content_type);
+}
+inline Result Client::Options(const char *path) { return cli_->Options(path); }
+inline Result Client::Options(const char *path, const Headers &headers) {
+ return cli_->Options(path, headers);
+}
+
+inline bool Client::send(const Request &req, Response &res) {
+ return cli_->send(req, res);
+}
+
+inline size_t Client::is_socket_open() const { return cli_->is_socket_open(); }
+
+inline void Client::stop() { cli_->stop(); }
+
+inline void Client::set_default_headers(Headers headers) {
+ cli_->set_default_headers(std::move(headers));
+}
+
+inline void Client::set_tcp_nodelay(bool on) { cli_->set_tcp_nodelay(on); }
+inline void Client::set_socket_options(SocketOptions socket_options) {
+ cli_->set_socket_options(std::move(socket_options));
+}
+
+inline void Client::set_connection_timeout(time_t sec, time_t usec) {
+ cli_->set_connection_timeout(sec, usec);
+}
+inline void Client::set_read_timeout(time_t sec, time_t usec) {
+ cli_->set_read_timeout(sec, usec);
+}
+inline void Client::set_write_timeout(time_t sec, time_t usec) {
+ cli_->set_write_timeout(sec, usec);
+}
+
+inline void Client::set_basic_auth(const char *username, const char *password) {
+ cli_->set_basic_auth(username, password);
+}
+inline void Client::set_bearer_token_auth(const char *token) {
+ cli_->set_bearer_token_auth(token);
+}
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+inline void Client::set_digest_auth(const char *username,
+ const char *password) {
+ cli_->set_digest_auth(username, password);
+}
+#endif
+
+inline void Client::set_keep_alive(bool on) { cli_->set_keep_alive(on); }
+inline void Client::set_follow_location(bool on) {
+ cli_->set_follow_location(on);
+}
+
+inline void Client::set_compress(bool on) { cli_->set_compress(on); }
+
+inline void Client::set_decompress(bool on) { cli_->set_decompress(on); }
+
+inline void Client::set_interface(const char *intf) {
+ cli_->set_interface(intf);
+}
+
+inline void Client::set_proxy(const char *host, int port) {
+ cli_->set_proxy(host, port);
+}
+inline void Client::set_proxy_basic_auth(const char *username,
+ const char *password) {
+ cli_->set_proxy_basic_auth(username, password);
+}
+inline void Client::set_proxy_bearer_token_auth(const char *token) {
+ cli_->set_proxy_bearer_token_auth(token);
+}
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+inline void Client::set_proxy_digest_auth(const char *username,
+ const char *password) {
+ cli_->set_proxy_digest_auth(username, password);
+}
+#endif
+
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+inline void Client::enable_server_certificate_verification(bool enabled) {
+ cli_->enable_server_certificate_verification(enabled);
+}
+#endif
+
+inline void Client::set_logger(Logger logger) { cli_->set_logger(logger); }
+
+#ifdef CPPHTTPLIB_OPENSSL_SUPPORT
+inline void Client::set_ca_cert_path(const char *ca_cert_file_path,
+ const char *ca_cert_dir_path) {
+ if (is_ssl_) {
+ static_cast<SSLClient &>(*cli_).set_ca_cert_path(ca_cert_file_path,
+ ca_cert_dir_path);
+ }
+}
+
+inline void Client::set_ca_cert_store(X509_STORE *ca_cert_store) {
+ if (is_ssl_) {
+ static_cast<SSLClient &>(*cli_).set_ca_cert_store(ca_cert_store);
+ }
+}
+
+inline long Client::get_openssl_verify_result() const {
+ if (is_ssl_) {
+ return static_cast<SSLClient &>(*cli_).get_openssl_verify_result();
+ }
+ return -1; // NOTE: -1 doesn't match any of X509_V_ERR_???
+}
+
+inline SSL_CTX *Client::ssl_context() const {
+ if (is_ssl_) { return static_cast<SSLClient &>(*cli_).ssl_context(); }
+ return nullptr;
+}
+#endif
+
// ----------------------------------------------------------------------------
} // namespace httplib
diff --git a/externals/inih/inih b/externals/inih/inih
-Subproject 603729dec89aaca42d7bd08f08bc333165b7d5d
+Subproject 1e80a47dffbda813604f0913e2ad68c7054c14e
diff --git a/externals/libressl b/externals/libressl
-Subproject 7d01cb01cb1a926ecb4c9c98b107ef3c26f59df
+Subproject 8289d0d07de6553bf4b900bf60e808ea3f7f59d
diff --git a/externals/libusb b/externals/libusb
deleted file mode 160000
-Subproject 3406d72cda879f8792a88bf5f6bd0b7a65636f7
diff --git a/externals/libusb/CMakeLists.txt b/externals/libusb/CMakeLists.txt
new file mode 100644
index 000000000..c0d24b126
--- /dev/null
+++ b/externals/libusb/CMakeLists.txt
@@ -0,0 +1,150 @@
+add_library(usb STATIC EXCLUDE_FROM_ALL
+ libusb/libusb/core.c
+ libusb/libusb/core.c
+ libusb/libusb/descriptor.c
+ libusb/libusb/hotplug.c
+ libusb/libusb/io.c
+ libusb/libusb/strerror.c
+ libusb/libusb/sync.c
+)
+set_target_properties(usb PROPERTIES VERSION 1.0.23)
+if(WIN32)
+ target_include_directories(usb
+ BEFORE
+ PUBLIC
+ libusb/libusb
+
+ PRIVATE
+ "${CMAKE_CURRENT_BINARY_DIR}"
+ )
+
+ if (NOT MINGW)
+ target_include_directories(usb BEFORE PRIVATE libusb/msvc)
+ endif()
+
+ # Works around other libraries providing their own definition of USB GUIDs (e.g. SDL2)
+ target_compile_definitions(usb PRIVATE "-DGUID_DEVINTERFACE_USB_DEVICE=(GUID){ 0xA5DCBF10, 0x6530, 0x11D2, {0x90, 0x1F, 0x00, 0xC0, 0x4F, 0xB9, 0x51, 0xED}}")
+else()
+target_include_directories(usb
+ # turns out other projects also have "config.h", so make sure the
+ # LibUSB one comes first
+ BEFORE
+
+ PUBLIC
+ libusb/libusb
+
+ PRIVATE
+ "${CMAKE_CURRENT_BINARY_DIR}"
+)
+endif()
+
+if(WIN32 OR CYGWIN)
+ target_sources(usb PRIVATE
+ libusb/libusb/os/threads_windows.c
+ libusb/libusb/os/windows_winusb.c
+ libusb/libusb/os/windows_usbdk.c
+ libusb/libusb/os/windows_nt_common.c
+ )
+ set(OS_WINDOWS TRUE)
+elseif(APPLE)
+ target_sources(usb PRIVATE
+ libusb/libusb/os/darwin_usb.c
+ )
+ find_library(COREFOUNDATION_LIBRARY CoreFoundation)
+ find_library(IOKIT_LIBRARY IOKit)
+ find_library(OBJC_LIBRARY objc)
+ target_link_libraries(usb PRIVATE
+ ${COREFOUNDATION_LIBRARY}
+ ${IOKIT_LIBRARY}
+ ${OBJC_LIBRARY}
+ )
+ set(OS_DARWIN TRUE)
+elseif(ANDROID)
+ target_sources(usb PRIVATE
+ libusb/libusb/os/linux_usbfs.c
+ libusb/libusb/os/linux_netlink.c
+ )
+ find_library(LOG_LIBRARY log)
+ target_link_libraries(usb PRIVATE ${LOG_LIBRARY})
+ set(OS_LINUX TRUE)
+elseif(${CMAKE_SYSTEM_NAME} MATCHES "Linux")
+ target_sources(usb PRIVATE
+ libusb/libusb/os/linux_usbfs.c
+ )
+ find_package(Libudev)
+ if(LIBUDEV_FOUND)
+ target_sources(usb PRIVATE
+ libusb/libusb/os/linux_udev.c
+ )
+ target_link_libraries(usb PRIVATE "${LIBUDEV_LIBRARIES}")
+ target_include_directories(usb PRIVATE "${LIBUDEV_INCLUDE_DIR}")
+ set(HAVE_LIBUDEV TRUE)
+ set(USE_UDEV TRUE)
+ else()
+ target_sources(usb PRIVATE
+ libusb/libusb/os/linux_netlink.c
+ )
+ endif()
+ set(OS_LINUX TRUE)
+elseif(${CMAKE_SYSTEM_NAME} MATCHES "NetBSD")
+ target_sources(usb PRIVATE
+ libusb/libusb/os/netbsd_usb.c
+ )
+ set(OS_NETBSD TRUE)
+elseif(${CMAKE_SYSTEM_NAME} MATCHES "OpenBSD")
+ target_sources(usb PRIVATE
+ libusb/libusb/os/openbsd_usb.c
+ )
+ set(OS_OPENBSD TRUE)
+endif()
+
+if(UNIX)
+ target_sources(usb PRIVATE
+ libusb/libusb/os/poll_posix.c
+ libusb/libusb/os/threads_posix.c
+ )
+ find_package(Threads REQUIRED)
+ if(THREADS_HAVE_PTHREAD_ARG)
+ target_compile_options(usb PUBLIC "-pthread")
+ endif()
+ if(CMAKE_THREAD_LIBS_INIT)
+ target_link_libraries(usb PRIVATE "${CMAKE_THREAD_LIBS_INIT}")
+ endif()
+ set(THREADS_POSIX TRUE)
+elseif(WIN32)
+ target_sources(usb PRIVATE
+ libusb/libusb/os/poll_windows.c
+ libusb/libusb/os/threads_windows.c
+ )
+endif()
+
+include(CheckFunctionExists)
+include(CheckIncludeFiles)
+include(CheckTypeSize)
+check_include_files(asm/types.h HAVE_ASM_TYPES_H)
+check_function_exists(gettimeofday HAVE_GETTIMEOFDAY)
+check_include_files(linux/filter.h HAVE_LINUX_FILTER_H)
+check_include_files(linux/netlink.h HAVE_LINUX_NETLINK_H)
+check_include_files(poll.h HAVE_POLL_H)
+check_include_files(signal.h HAVE_SIGNAL_H)
+check_include_files(strings.h HAVE_STRINGS_H)
+check_type_size("struct timespec" STRUCT_TIMESPEC)
+check_function_exists(syslog HAVE_SYSLOG_FUNC)
+check_include_files(syslog.h HAVE_SYSLOG_H)
+check_include_files(sys/socket.h HAVE_SYS_SOCKET_H)
+check_include_files(sys/time.h HAVE_SYS_TIME_H)
+check_include_files(sys/types.h HAVE_SYS_TYPES_H)
+
+set(CMAKE_EXTRA_INCLUDE_FILES poll.h)
+check_type_size("nfds_t" nfds_t)
+unset(CMAKE_EXTRA_INCLUDE_FILES)
+if(HAVE_NFDS_T)
+ set(POLL_NFDS_TYPE "nfds_t")
+else()
+ set(POLL_NFDS_TYPE "unsigned int")
+endif()
+
+check_include_files(sys/timerfd.h USBI_TIMERFD_AVAILABLE)
+
+
+configure_file(config.h.in config.h)
diff --git a/externals/libusb/config.h.in b/externals/libusb/config.h.in
new file mode 100644
index 000000000..915b7390f
--- /dev/null
+++ b/externals/libusb/config.h.in
@@ -0,0 +1,90 @@
+/* Default visibility */
+#if defined(__GNUC__) || defined(__clang__)
+ #define DEFAULT_VISIBILITY __attribute__((visibility("default")))
+#elif defined(_MSC_VER)
+ #define DEFAULT_VISIBILITY __declspec(dllexport)
+#endif
+
+/* Start with debug message logging enabled */
+#undef ENABLE_DEBUG_LOGGING
+
+/* Message logging */
+#undef ENABLE_LOGGING
+
+/* Define to 1 if you have the <asm/types.h> header file. */
+#cmakedefine HAVE_ASM_TYPES_H 1
+
+/* Define to 1 if you have the `gettimeofday' function. */
+#cmakedefine HAVE_GETTIMEOFDAY 1
+
+/* Define to 1 if you have the `udev' library (-ludev). */
+#cmakedefine HAVE_LIBUDEV 1
+
+/* Define to 1 if you have the <linux/filter.h> header file. */
+#cmakedefine HAVE_LINUX_FILTER_H 1
+
+/* Define to 1 if you have the <linux/netlink.h> header file. */
+#cmakedefine HAVE_LINUX_NETLINK_H 1
+
+/* Define to 1 if you have the <poll.h> header file. */
+#cmakedefine HAVE_POLL_H 1
+
+/* Define to 1 if you have the <signal.h> header file. */
+#cmakedefine HAVE_SIGNAL_H 1
+
+/* Define to 1 if you have the <strings.h> header file. */
+#cmakedefine HAVE_STRINGS_H 1
+
+/* Define to 1 if the system has the type `struct timespec'. */
+#cmakedefine HAVE_STRUCT_TIMESPEC 1
+
+/* syslog() function available */
+#cmakedefine HAVE_SYSLOG_FUNC 1
+
+/* Define to 1 if you have the <syslog.h> header file. */
+#cmakedefine HAVE_SYSLOG_H 1
+
+/* Define to 1 if you have the <sys/socket.h> header file. */
+#cmakedefine HAVE_SYS_SOCKET_H 1
+
+/* Define to 1 if you have the <sys/time.h> header file. */
+#cmakedefine HAVE_SYS_TIME_H 1
+
+/* Define to 1 if you have the <sys/types.h> header file. */
+#cmakedefine HAVE_SYS_TYPES_H 1
+
+/* Darwin backend */
+#cmakedefine OS_DARWIN 1
+
+/* Linux backend */
+#cmakedefine OS_LINUX 1
+
+/* NetBSD backend */
+#cmakedefine OS_NETBSD 1
+
+/* OpenBSD backend */
+#cmakedefine OS_OPENBSD 1
+
+/* Windows backend */
+#cmakedefine OS_WINDOWS 1
+
+/* type of second poll() argument */
+#define POLL_NFDS_TYPE @POLL_NFDS_TYPE@
+
+/* Use POSIX Threads */
+#cmakedefine THREADS_POSIX
+
+/* timerfd headers available */
+#cmakedefine USBI_TIMERFD_AVAILABLE 1
+
+/* Enable output to system log */
+#define USE_SYSTEM_LOGGING_FACILITY 1
+
+/* Use udev for device enumeration/hotplug */
+#cmakedefine USE_UDEV 1
+
+/* Use GNU extensions */
+#define _GNU_SOURCE
+
+/* Oldest Windows version supported */
+#define WINVER 0x0501
diff --git a/externals/libusb/libusb b/externals/libusb/libusb
new file mode 160000
+Subproject e782eeb2514266f6738e242cdcb18e3ae1ed06f
diff --git a/externals/lurlparser/CMakeLists.txt b/externals/lurlparser/CMakeLists.txt
deleted file mode 100644
index 45046ffd3..000000000
--- a/externals/lurlparser/CMakeLists.txt
+++ /dev/null
@@ -1,8 +0,0 @@
-add_library(lurlparser
- LUrlParser.cpp
- LUrlParser.h
-)
-
-create_target_directory_groups(lurlparser)
-
-target_include_directories(lurlparser INTERFACE ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/externals/lurlparser/LUrlParser.cpp b/externals/lurlparser/LUrlParser.cpp
deleted file mode 100644
index 9c134e330..000000000
--- a/externals/lurlparser/LUrlParser.cpp
+++ /dev/null
@@ -1,265 +0,0 @@
-/*
- * Lightweight URL & URI parser (RFC 1738, RFC 3986)
- * https://github.com/corporateshark/LUrlParser
- *
- * The MIT License (MIT)
- *
- * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#include "LUrlParser.h"
-
-#include <algorithm>
-#include <cstring>
-#include <stdlib.h>
-
-// check if the scheme name is valid
-static bool IsSchemeValid( const std::string& SchemeName )
-{
- for ( auto c : SchemeName )
- {
- if ( !isalpha( c ) && c != '+' && c != '-' && c != '.' ) return false;
- }
-
- return true;
-}
-
-bool LUrlParser::clParseURL::GetPort( int* OutPort ) const
-{
- if ( !IsValid() ) { return false; }
-
- int Port = atoi( m_Port.c_str() );
-
- if ( Port <= 0 || Port > 65535 ) { return false; }
-
- if ( OutPort ) { *OutPort = Port; }
-
- return true;
-}
-
-// based on RFC 1738 and RFC 3986
-LUrlParser::clParseURL LUrlParser::clParseURL::ParseURL( const std::string& URL )
-{
- LUrlParser::clParseURL Result;
-
- const char* CurrentString = URL.c_str();
-
- /*
- * <scheme>:<scheme-specific-part>
- * <scheme> := [a-z\+\-\.]+
- * For resiliency, programs interpreting URLs should treat upper case letters as equivalent to lower case in scheme names
- */
-
- // try to read scheme
- {
- const char* LocalString = strchr( CurrentString, ':' );
-
- if ( !LocalString )
- {
- return clParseURL( LUrlParserError_NoUrlCharacter );
- }
-
- // save the scheme name
- Result.m_Scheme = std::string( CurrentString, LocalString - CurrentString );
-
- if ( !IsSchemeValid( Result.m_Scheme ) )
- {
- return clParseURL( LUrlParserError_InvalidSchemeName );
- }
-
- // scheme should be lowercase
- std::transform( Result.m_Scheme.begin(), Result.m_Scheme.end(), Result.m_Scheme.begin(), ::tolower );
-
- // skip ':'
- CurrentString = LocalString+1;
- }
-
- /*
- * //<user>:<password>@<host>:<port>/<url-path>
- * any ":", "@" and "/" must be normalized
- */
-
- // skip "//"
- if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
- if ( *CurrentString++ != '/' ) return clParseURL( LUrlParserError_NoDoubleSlash );
-
- // check if the user name and password are specified
- bool bHasUserName = false;
-
- const char* LocalString = CurrentString;
-
- while ( *LocalString )
- {
- if ( *LocalString == '@' )
- {
- // user name and password are specified
- bHasUserName = true;
- break;
- }
- else if ( *LocalString == '/' )
- {
- // end of <host>:<port> specification
- bHasUserName = false;
- break;
- }
-
- LocalString++;
- }
-
- // user name and password
- LocalString = CurrentString;
-
- if ( bHasUserName )
- {
- // read user name
- while ( *LocalString && *LocalString != ':' && *LocalString != '@' ) LocalString++;
-
- Result.m_UserName = std::string( CurrentString, LocalString - CurrentString );
-
- // proceed with the current pointer
- CurrentString = LocalString;
-
- if ( *CurrentString == ':' )
- {
- // skip ':'
- CurrentString++;
-
- // read password
- LocalString = CurrentString;
-
- while ( *LocalString && *LocalString != '@' ) LocalString++;
-
- Result.m_Password = std::string( CurrentString, LocalString - CurrentString );
-
- CurrentString = LocalString;
- }
-
- // skip '@'
- if ( *CurrentString != '@' )
- {
- return clParseURL( LUrlParserError_NoAtSign );
- }
-
- CurrentString++;
- }
-
- bool bHasBracket = ( *CurrentString == '[' );
-
- // go ahead, read the host name
- LocalString = CurrentString;
-
- while ( *LocalString )
- {
- if ( bHasBracket && *LocalString == ']' )
- {
- // end of IPv6 address
- LocalString++;
- break;
- }
- else if ( !bHasBracket && ( *LocalString == ':' || *LocalString == '/' ) )
- {
- // port number is specified
- break;
- }
-
- LocalString++;
- }
-
- Result.m_Host = std::string( CurrentString, LocalString - CurrentString );
-
- CurrentString = LocalString;
-
- // is port number specified?
- if ( *CurrentString == ':' )
- {
- CurrentString++;
-
- // read port number
- LocalString = CurrentString;
-
- while ( *LocalString && *LocalString != '/' ) LocalString++;
-
- Result.m_Port = std::string( CurrentString, LocalString - CurrentString );
-
- CurrentString = LocalString;
- }
-
- // end of string
- if ( !*CurrentString )
- {
- Result.m_ErrorCode = LUrlParserError_Ok;
-
- return Result;
- }
-
- // skip '/'
- if ( *CurrentString != '/' )
- {
- return clParseURL( LUrlParserError_NoSlash );
- }
-
- CurrentString++;
-
- // parse the path
- LocalString = CurrentString;
-
- while ( *LocalString && *LocalString != '#' && *LocalString != '?' ) LocalString++;
-
- Result.m_Path = std::string( CurrentString, LocalString - CurrentString );
-
- CurrentString = LocalString;
-
- // check for query
- if ( *CurrentString == '?' )
- {
- // skip '?'
- CurrentString++;
-
- // read query
- LocalString = CurrentString;
-
- while ( *LocalString && *LocalString != '#' ) LocalString++;
-
- Result.m_Query = std::string( CurrentString, LocalString - CurrentString );
-
- CurrentString = LocalString;
- }
-
- // check for fragment
- if ( *CurrentString == '#' )
- {
- // skip '#'
- CurrentString++;
-
- // read fragment
- LocalString = CurrentString;
-
- while ( *LocalString ) LocalString++;
-
- Result.m_Fragment = std::string( CurrentString, LocalString - CurrentString );
-
- CurrentString = LocalString;
- }
-
- Result.m_ErrorCode = LUrlParserError_Ok;
-
- return Result;
-}
diff --git a/externals/lurlparser/LUrlParser.h b/externals/lurlparser/LUrlParser.h
deleted file mode 100644
index 25d210981..000000000
--- a/externals/lurlparser/LUrlParser.h
+++ /dev/null
@@ -1,78 +0,0 @@
-/*
- * Lightweight URL & URI parser (RFC 1738, RFC 3986)
- * https://github.com/corporateshark/LUrlParser
- *
- * The MIT License (MIT)
- *
- * Copyright (C) 2015 Sergey Kosarevsky (sk@linderdaum.com)
- *
- * Permission is hereby granted, free of charge, to any person obtaining a copy
- * of this software and associated documentation files (the "Software"), to deal
- * in the Software without restriction, including without limitation the rights
- * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
- * copies of the Software, and to permit persons to whom the Software is
- * furnished to do so, subject to the following conditions:
- *
- * The above copyright notice and this permission notice shall be included in all
- * copies or substantial portions of the Software.
- *
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
- * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
- * SOFTWARE.
- */
-
-#pragma once
-
-#include <string>
-
-namespace LUrlParser
-{
-enum LUrlParserError
-{
- LUrlParserError_Ok = 0,
- LUrlParserError_Uninitialized = 1,
- LUrlParserError_NoUrlCharacter = 2,
- LUrlParserError_InvalidSchemeName = 3,
- LUrlParserError_NoDoubleSlash = 4,
- LUrlParserError_NoAtSign = 5,
- LUrlParserError_UnexpectedEndOfLine = 6,
- LUrlParserError_NoSlash = 7,
-};
-
-class clParseURL
-{
-public:
- LUrlParserError m_ErrorCode;
- std::string m_Scheme;
- std::string m_Host;
- std::string m_Port;
- std::string m_Path;
- std::string m_Query;
- std::string m_Fragment;
- std::string m_UserName;
- std::string m_Password;
-
- clParseURL()
- : m_ErrorCode( LUrlParserError_Uninitialized )
- {}
-
- /// return 'true' if the parsing was successful
- bool IsValid() const { return m_ErrorCode == LUrlParserError_Ok; }
-
- /// helper to convert the port number to int, return 'true' if the port is valid (within the 0..65535 range)
- bool GetPort( int* OutPort ) const;
-
- /// parse the URL
- static clParseURL ParseURL( const std::string& URL );
-
-private:
- explicit clParseURL( LUrlParserError ErrorCode )
- : m_ErrorCode( ErrorCode )
- {}
-};
-
-} // namespace LUrlParser
diff --git a/externals/lurlparser/README.md b/externals/lurlparser/README.md
deleted file mode 100644
index be7f0135a..000000000
--- a/externals/lurlparser/README.md
+++ /dev/null
@@ -1,19 +0,0 @@
-From https://github.com/corporateshark/LUrlParser/commit/455d5e2d27e3946f11ad0328fee9ee2628e6a8e2
-
-MIT License
-
-===
-
-Lightweight URL & URI parser (RFC 1738, RFC 3986)
-
-(C) Sergey Kosarevsky, 2015
-
-@corporateshark sk@linderdaum.com
-
-http://www.linderdaum.com
-
-http://blog.linderdaum.com
-
-=============================
-
-A tiny and lightweight URL & URI parser (RFC 1738, RFC 3986) written in C++.
diff --git a/externals/microprofile/microprofile.h b/externals/microprofile/microprofile.h
index 0c0d0a4d3..a06f6457d 100644
--- a/externals/microprofile/microprofile.h
+++ b/externals/microprofile/microprofile.h
@@ -152,9 +152,11 @@ typedef uint16_t MicroProfileGroupId;
#include <stdint.h>
#include <string.h>
-#include <thread>
-#include <mutex>
+#include <algorithm>
+#include <array>
#include <atomic>
+#include <mutex>
+#include <thread>
#ifndef MICROPROFILE_API
#define MICROPROFILE_API
@@ -605,28 +607,45 @@ struct MicroProfileFrameState
struct MicroProfileThreadLog
{
- MicroProfileLogEntry Log[MICROPROFILE_BUFFER_SIZE];
+ std::array<MicroProfileLogEntry, MICROPROFILE_BUFFER_SIZE> Log{};
- std::atomic<uint32_t> nPut;
- std::atomic<uint32_t> nGet;
- uint32_t nActive;
- uint32_t nGpu;
- ThreadIdType nThreadId;
+ std::atomic<uint32_t> nPut{0};
+ std::atomic<uint32_t> nGet{0};
+ uint32_t nActive = 0;
+ uint32_t nGpu = 0;
+ ThreadIdType nThreadId{};
- uint32_t nStack[MICROPROFILE_STACK_MAX];
- int64_t nChildTickStack[MICROPROFILE_STACK_MAX];
- uint32_t nStackPos;
+ std::array<uint32_t, MICROPROFILE_STACK_MAX> nStack{};
+ std::array<int64_t, MICROPROFILE_STACK_MAX> nChildTickStack{};
+ uint32_t nStackPos = 0;
- uint8_t nGroupStackPos[MICROPROFILE_MAX_GROUPS];
- int64_t nGroupTicks[MICROPROFILE_MAX_GROUPS];
- int64_t nAggregateGroupTicks[MICROPROFILE_MAX_GROUPS];
+ std::array<uint8_t, MICROPROFILE_MAX_GROUPS> nGroupStackPos{};
+ std::array<int64_t, MICROPROFILE_MAX_GROUPS> nGroupTicks{};
+ std::array<int64_t, MICROPROFILE_MAX_GROUPS> nAggregateGroupTicks{};
enum
{
THREAD_MAX_LEN = 64,
};
- char ThreadName[64];
- int nFreeListNext;
+ char ThreadName[64]{};
+ int nFreeListNext = 0;
+
+ void Reset() {
+ Log.fill({});
+ nPut = 0;
+ nGet = 0;
+ nActive = 0;
+ nGpu = 0;
+ nThreadId = {};
+ nStack.fill(0);
+ nChildTickStack.fill(0);
+ nStackPos = 0;
+ nGroupStackPos.fill(0);
+ nGroupTicks.fill(0);
+ nAggregateGroupTicks.fill(0);
+ std::fill(std::begin(ThreadName), std::end(ThreadName), '\0');
+ nFreeListNext = 0;
+ }
};
#if MICROPROFILE_GPU_TIMERS_D3D11
@@ -883,8 +902,10 @@ inline uint16_t MicroProfileGetGroupIndex(MicroProfileToken t)
#include <windows.h>
#define snprintf _snprintf
+#ifdef _MSC_VER
#pragma warning(push)
#pragma warning(disable: 4244)
+#endif
int64_t MicroProfileTicksPerSecondCpu()
{
static int64_t nTicksPerSecond = 0;
@@ -927,7 +948,11 @@ typedef HANDLE MicroProfileThread;
DWORD _stdcall ThreadTrampoline(void* pFunc)
{
MicroProfileThreadFunc F = (MicroProfileThreadFunc)pFunc;
- return (uint32_t)F(0);
+
+ // The return value of F will always return a void*, however, this is for
+ // compatibility with pthreads. The underlying "address" of the pointer
+ // is always a 32-bit value, so this cast is safe to perform.
+ return static_cast<DWORD>(reinterpret_cast<uint64_t>(F(0)));
}
inline void MicroProfileThreadStart(MicroProfileThread* pThread, MicroProfileThreadFunc Func)
@@ -1018,7 +1043,7 @@ static void MicroProfileCreateThreadLogKey()
#else
MP_THREAD_LOCAL MicroProfileThreadLog* g_MicroProfileThreadLog = 0;
#endif
-static bool g_bUseLock = false; /// This is used because windows does not support using mutexes under dll init(which is where global initialization is handled)
+static std::atomic<bool> g_bUseLock{false}; /// This is used because windows does not support using mutexes under dll init(which is where global initialization is handled)
MICROPROFILE_DEFINE(g_MicroProfileFlip, "MicroProfile", "MicroProfileFlip", 0x3355ee);
@@ -1151,6 +1176,7 @@ MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName)
MP_ASSERT(pLog->nPut.load() == 0);
MP_ASSERT(pLog->nGet.load() == 0);
S.nFreeListHead = S.Pool[S.nFreeListHead]->nFreeListNext;
+ pLog->Reset();
}
else
{
@@ -1158,7 +1184,6 @@ MicroProfileThreadLog* MicroProfileCreateThreadLog(const char* pName)
S.nMemUsage += sizeof(MicroProfileThreadLog);
S.Pool[S.nNumLogs++] = pLog;
}
- memset(pLog, 0, sizeof(*pLog));
int len = (int)strlen(pName);
int maxlen = sizeof(pLog->ThreadName)-1;
len = len < maxlen ? len : maxlen;
@@ -1206,8 +1231,8 @@ void MicroProfileOnThreadExit()
{
S.Frames[i].nLogStart[nLogIndex] = 0;
}
- memset(pLog->nGroupStackPos, 0, sizeof(pLog->nGroupStackPos));
- memset(pLog->nGroupTicks, 0, sizeof(pLog->nGroupTicks));
+ pLog->nGroupStackPos.fill(0);
+ pLog->nGroupTicks.fill(0);
}
}
@@ -1723,10 +1748,10 @@ void MicroProfileFlip()
}
}
}
- for(uint32_t i = 0; i < MICROPROFILE_MAX_GROUPS; ++i)
+ for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
{
- pLog->nGroupTicks[i] += nGroupTicks[i];
- pFrameGroup[i] += nGroupTicks[i];
+ pLog->nGroupTicks[j] += nGroupTicks[j];
+ pFrameGroup[j] += nGroupTicks[j];
}
pLog->nStackPos = nStackPos;
}
@@ -3309,7 +3334,7 @@ bool MicroProfileIsLocalThread(uint32_t nThreadId)
#endif
#else
-bool MicroProfileIsLocalThread(uint32_t nThreadId){return false;}
+bool MicroProfileIsLocalThread([[maybe_unused]] uint32_t nThreadId) { return false; }
void MicroProfileStopContextSwitchTrace(){}
void MicroProfileStartContextSwitchTrace(){}
@@ -3557,7 +3582,7 @@ int MicroProfileGetGpuTickReference(int64_t* pOutCpu, int64_t* pOutGpu)
#undef S
-#ifdef _WIN32
+#ifdef _MSC_VER
#pragma warning(pop)
#endif
diff --git a/externals/microprofile/microprofileui.h b/externals/microprofile/microprofileui.h
index fe2410cf4..85fbf2cb9 100644
--- a/externals/microprofile/microprofileui.h
+++ b/externals/microprofile/microprofileui.h
@@ -169,14 +169,13 @@ MICROPROFILEUI_API void MicroProfileCustomGroup(const char* pCustomName, uint32_
MICROPROFILEUI_API void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer);
#ifdef MICROPROFILEUI_IMPL
-#ifdef _WIN32
-#define snprintf _snprintf
-#endif
+#include <inttypes.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <math.h>
#include <algorithm>
+#include <array>
MICROPROFILE_DEFINE(g_MicroProfileDetailed, "MicroProfile", "Detailed View", 0x8888000);
MICROPROFILE_DEFINE(g_MicroProfileDrawGraph, "MicroProfile", "Draw Graph", 0xff44ee00);
@@ -227,10 +226,10 @@ struct SOptionDesc
uint8_t nIndex;
bool bSelected;
};
-static uint32_t g_MicroProfileAggregatePresets[] = {0, 10, 20, 30, 60, 120};
-static float g_MicroProfileReferenceTimePresets[] = {5.f, 10.f, 15.f,20.f, 33.33f, 66.66f, 100.f, 250.f, 500.f, 1000.f};
-static uint32_t g_MicroProfileOpacityPresets[] = {0x40, 0x80, 0xc0, 0xff};
-static const char* g_MicroProfilePresetNames[] =
+static const std::array<uint32_t, 6> g_MicroProfileAggregatePresets{0, 10, 20, 30, 60, 120};
+static const std::array<float, 10> g_MicroProfileReferenceTimePresets{5.f, 10.f, 15.f,20.f, 33.33f, 66.66f, 100.f, 250.f, 500.f, 1000.f};
+static const std::array<uint32_t, 4> g_MicroProfileOpacityPresets{0x40, 0x80, 0xc0, 0xff};
+static const std::array<const char*, 7> g_MicroProfilePresetNames
{
MICROPROFILE_DEFAULT_PRESET,
"Render",
@@ -243,8 +242,8 @@ static const char* g_MicroProfilePresetNames[] =
enum
{
- MICROPROFILE_NUM_REFERENCE_PRESETS = sizeof(g_MicroProfileReferenceTimePresets)/sizeof(g_MicroProfileReferenceTimePresets[0]),
- MICROPROFILE_NUM_OPACITY_PRESETS = sizeof(g_MicroProfileOpacityPresets)/sizeof(g_MicroProfileOpacityPresets[0]),
+ MICROPROFILE_NUM_REFERENCE_PRESETS = g_MicroProfileReferenceTimePresets.size(),
+ MICROPROFILE_NUM_OPACITY_PRESETS = g_MicroProfileOpacityPresets.size(),
#if MICROPROFILE_CONTEXT_SWITCH_TRACE
MICROPROFILE_OPTION_SIZE = MICROPROFILE_NUM_REFERENCE_PRESETS + MICROPROFILE_NUM_OPACITY_PRESETS * 2 + 2 + 7,
#else
@@ -326,9 +325,9 @@ struct MicroProfileUI
MicroProfileUI g_MicroProfileUI;
#define UI g_MicroProfileUI
-static uint32_t g_nMicroProfileBackColors[2] = { 0x474747, 0x313131 };
+static const std::array<uint32_t, 2> g_nMicroProfileBackColors{ 0x474747, 0x313131 };
#define MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS 16
-static uint32_t g_nMicroProfileContextSwitchThreadColors[MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS] = //palette generated by http://tools.medialab.sciences-po.fr/iwanthue/index.php
+static const std::array<uint32_t, MICROPROFILE_NUM_CONTEXT_SWITCH_COLORS> g_nMicroProfileContextSwitchThreadColors //palette generated by http://tools.medialab.sciences-po.fr/iwanthue/index.php
{
0x63607B,
0x755E2B,
@@ -356,7 +355,7 @@ void MicroProfileInitUI()
{
bInitialized = true;
memset(&g_MicroProfileUI, 0, sizeof(g_MicroProfileUI));
- UI.nActiveMenu = (uint32_t)-1;
+ UI.nActiveMenu = UINT32_MAX;
UI.fDetailedOffsetTarget = UI.fDetailedOffset = 0.f;
UI.fDetailedRangeTarget = UI.fDetailedRange = 50.f;
@@ -368,7 +367,7 @@ void MicroProfileInitUI()
UI.nWidth = 100;
UI.nHeight = 100;
- UI.nCustomActive = (uint32_t)-1;
+ UI.nCustomActive = UINT32_MAX;
UI.nCustomTimerCount = 0;
UI.nCustomCount = 0;
@@ -498,8 +497,8 @@ inline void MicroProfileDrawFloatWindow(uint32_t nX, uint32_t nY, const char** p
{
MicroProfileDrawBox(nX-MICROPROFILE_TEXT_WIDTH, nY, nX, nY + MICROPROFILE_TEXT_WIDTH, pColors[i]|0xff000000);
}
- MicroProfileDrawText(nX + 1, nY + 1, (uint32_t)-1, ppStrings[i0], (uint32_t)strlen(ppStrings[i0]));
- MicroProfileDrawText(nX + nWidth - nStringLengths[i0+1] * (MICROPROFILE_TEXT_WIDTH+1), nY + 1, (uint32_t)-1, ppStrings[i0+1], (uint32_t)strlen(ppStrings[i0+1]));
+ MicroProfileDrawText(nX + 1, nY + 1, UINT32_MAX, ppStrings[i0], (uint32_t)strlen(ppStrings[i0]));
+ MicroProfileDrawText(nX + nWidth - nStringLengths[i0+1] * (MICROPROFILE_TEXT_WIDTH+1), nY + 1, UINT32_MAX, ppStrings[i0+1], (uint32_t)strlen(ppStrings[i0+1]));
nY += (MICROPROFILE_TEXT_HEIGHT+1);
}
}
@@ -522,7 +521,7 @@ inline void MicroProfileDrawTextBox(uint32_t nX, uint32_t nY, const char** ppStr
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000);
for(uint32_t i = 0; i < nNumStrings; ++i)
{
- MicroProfileDrawText(nX + 1, nY + 1, (uint32_t)-1, ppStrings[i], (uint32_t)strlen(ppStrings[i]));
+ MicroProfileDrawText(nX + 1, nY + 1, UINT32_MAX, ppStrings[i], (uint32_t)strlen(ppStrings[i]));
nY += (MICROPROFILE_TEXT_HEIGHT+1);
}
}
@@ -781,7 +780,7 @@ inline void MicroProfileDrawDetailedContextSwitchBars(uint32_t nY, uint32_t nThr
{
MicroProfile& S = *MicroProfileGet();
int64_t nTickIn = -1;
- uint32_t nThreadBefore = -1;
+ uint32_t nThreadBefore = UINT32_MAX;
float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
float fMsToScreen = UI.nWidth / UI.fDetailedRange;
float fMouseX = (float)UI.nMouseX;
@@ -949,10 +948,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
uint32_t nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadAfter;
uint32_t nContextSwitchHoverThreadBefore = S.nContextSwitchHoverThreadBefore;
- S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = -1;
+ S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = UINT32_MAX;
- uint32_t nContextSwitchStart = -1;
- uint32_t nContextSwitchEnd = -1;
+ uint32_t nContextSwitchStart = UINT32_MAX;
+ uint32_t nContextSwitchEnd = UINT32_MAX;
S.nContextSwitchHoverCpuNext = 0xff;
S.nContextSwitchHoverTickIn = -1;
S.nContextSwitchHoverTickOut = -1;
@@ -1005,9 +1004,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
}while(pFrameLogFirst != pFrameFirst);
- if(nGet == (uint32_t)-1)
+ if (nGet == UINT32_MAX) {
continue;
- MP_ASSERT(nGet != (uint32_t)-1);
+ }
+ MP_ASSERT(nGet != UINT32_MAX);
nPut = pFrameLogLast->nLogStart[i];
@@ -1023,9 +1023,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
int64_t nBaseTicks = bGpu ? nBaseTicksGpu : nBaseTicksCpu;
char ThreadName[MicroProfileThreadLog::THREAD_MAX_LEN + 16];
uint64_t nThreadId = pLog->nThreadId;
- snprintf(ThreadName, sizeof(ThreadName)-1, "%04llx: %s", nThreadId, &pLog->ThreadName[0] );
+ snprintf(ThreadName, sizeof(ThreadName)-1, "%04" PRIx64 ": %s", nThreadId, &pLog->ThreadName[0] );
nY += 3;
- uint32_t nThreadColor = -1;
+ uint32_t nThreadColor = UINT32_MAX;
if(pLog->nThreadId == nContextSwitchHoverThreadAfter || pLog->nThreadId == nContextSwitchHoverThreadBefore)
nThreadColor = UI.nHoverColorShared|0x906060;
MicroProfileDrawText(0, nY, nThreadColor, &ThreadName[0], (uint32_t)strlen(&ThreadName[0]));
@@ -1048,7 +1048,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
uint32_t nEnd = nRange[j][1];
for(uint32_t k = nStart; k < nEnd; ++k)
{
- MicroProfileLogEntry* pEntry = pLog->Log + k;
+ MicroProfileLogEntry* pEntry = &pLog->Log[k];
int nType = MicroProfileLogType(*pEntry);
if(MP_LOG_ENTER == nType)
{
@@ -1066,7 +1066,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
continue;
}
- MicroProfileLogEntry* pEntryEnter = pLog->Log + nStack[nStackPos-1];
+ MicroProfileLogEntry* pEntryEnter = &pLog->Log[nStack[nStackPos-1]];
if(MicroProfileLogTimerIndex(*pEntryEnter) != MicroProfileLogTimerIndex(*pEntry))
{
//uprintf("mismatch %llx %llx\n", pEntryEnter->nToken, pEntry->nToken);
@@ -1126,7 +1126,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
uint32_t nIntegerWidth = (uint32_t)(fXEnd - fXStart);
if(nIntegerWidth)
{
- if(bHover && UI.nActiveMenu == -1)
+ if(bHover && UI.nActiveMenu == UINT32_MAX)
{
nHoverToken = MicroProfileLogTimerIndex(*pEntry);
#if MICROPROFILE_DEBUG
@@ -1146,7 +1146,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
int nCharacters = (nTextWidth - 2*MICROPROFILE_TEXT_WIDTH) / MICROPROFILE_TEXT_WIDTH;
if(nCharacters>0)
{
- MicroProfileDrawText(fXStartText+1, fYStart+1, -1, S.TimerInfo[nTimerIndex].pName, MicroProfileMin<uint32_t>(S.TimerInfo[nTimerIndex].nNameLen, nCharacters));
+ MicroProfileDrawText(fXStartText + 1, fYStart + 1, UINT32_MAX, S.TimerInfo[nTimerIndex].pName, MicroProfileMin<uint32_t>(S.TimerInfo[nTimerIndex].nNameLen, nCharacters));
}
}
#endif
@@ -1158,7 +1158,7 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
int nLineX = (int)floor(fXAvg+0.5f);
if(nLineX != (int)nLinesDrawn[nStackPos])
{
- if(bHover && UI.nActiveMenu == -1)
+ if(bHover && UI.nActiveMenu == UINT32_MAX)
{
nHoverToken = (uint32_t)MicroProfileLogTimerIndex(*pEntry);
nHoverTime = MicroProfileLogTickDifference(nTickStart, nTickEnd);
@@ -1235,9 +1235,9 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
// nThreadId is 32-bit on Windows
int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04x: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
#else
- int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04llx: %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
+ int nStrLen = snprintf(ThreadName, sizeof(ThreadName)-1, "%04" PRIx64 ": %s%s", nThreadId, cLocal, i < nNumThreadsBase ? &S.Pool[i]->ThreadName[0] : MICROPROFILE_THREAD_NAME_FROM_ID(nThreadId) );
#endif
- uint32_t nThreadColor = -1;
+ uint32_t nThreadColor = UINT32_MAX;
if(nThreadId == nContextSwitchHoverThreadAfter || nThreadId == nContextSwitchHoverThreadBefore)
nThreadColor = UI.nHoverColorShared|0x906060;
MicroProfileDrawDetailedContextSwitchBars(nY+2, nThreadId, nContextSwitchStart, nContextSwitchEnd, nBaseTicksCpu, nBaseY);
@@ -1249,9 +1249,6 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
S.nContextSwitchHoverCpu = S.nContextSwitchHoverCpuNext;
-
-
-
UI.pDisplayMouseOver = pMouseOverNext;
if(!S.nRunning)
@@ -1286,10 +1283,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
float fStartTextX = fXStart - fStartTextWidth - 2;
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
- MicroProfileDrawText(fStartTextX+1, nBaseY, (uint32_t)-1, sBuffer, nLenStart);
+ MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
- MicroProfileDrawText(fXEnd+2, nBaseY+1, (uint32_t)-1, sBuffer, nLenEnd);
+ MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
if(UI.nMouseRight)
{
@@ -1316,10 +1313,10 @@ inline void MicroProfileDrawDetailedBars(uint32_t nWidth, uint32_t nHeight, int
float fStartTextWidth = (float)((1+MICROPROFILE_TEXT_WIDTH) * nLenStart);
float fStartTextX = fXStart - fStartTextWidth - 2;
MicroProfileDrawBox(fStartTextX, nBaseY, fStartTextX + fStartTextWidth + 2, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
- MicroProfileDrawText(fStartTextX+1, nBaseY, (uint32_t)-1, sBuffer, nLenStart);
+ MicroProfileDrawText(fStartTextX+1, nBaseY, UINT32_MAX, sBuffer, nLenStart);
uint32_t nLenEnd = snprintf(sBuffer, sizeof(sBuffer)-1, "%.2fms", fMsEnd);
MicroProfileDrawBox(fXEnd+1, nBaseY, fXEnd+1+(1+MICROPROFILE_TEXT_WIDTH) * nLenEnd + 3, MICROPROFILE_TEXT_HEIGHT + 2 + nBaseY, 0x33000000, MicroProfileBoxTypeFlat);
- MicroProfileDrawText(fXEnd+2, nBaseY+1, (uint32_t)-1, sBuffer, nLenEnd);
+ MicroProfileDrawText(fXEnd+2, nBaseY+1, UINT32_MAX, sBuffer, nLenEnd);
}
}
}
@@ -1365,7 +1362,7 @@ inline void MicroProfileDrawDetailedFrameHistory(uint32_t nWidth, uint32_t nHeig
fBaseX = fXStart;
uint32_t nColor = MICROPROFILE_FRAME_HISTORY_COLOR_CPU;
if(nIndex == nSelectedFrame)
- nColor = (uint32_t)-1;
+ nColor = UINT32_MAX;
MicroProfileDrawBox(fXStart, nBaseY + fScale * nBarHeight, fXEnd, nBaseY+MICROPROFILE_FRAME_HISTORY_HEIGHT, nColor, MicroProfileBoxTypeBar);
if(pNext->nFrameStartCpu > nCpuStart)
{
@@ -1387,7 +1384,7 @@ inline void MicroProfileDrawDetailedView(uint32_t nWidth, uint32_t nHeight)
uint32_t nBaseY = MICROPROFILE_TEXT_HEIGHT + 1;
int nSelectedFrame = -1;
- if(UI.nMouseY > nBaseY && UI.nMouseY <= nBaseY + MICROPROFILE_FRAME_HISTORY_HEIGHT && UI.nActiveMenu == -1)
+ if(UI.nMouseY > nBaseY && UI.nMouseY <= nBaseY + MICROPROFILE_FRAME_HISTORY_HEIGHT && UI.nActiveMenu == UINT32_MAX)
{
nSelectedFrame = ((MICROPROFILE_NUM_FRAMES) * (UI.nWidth-UI.nMouseX) / UI.nWidth);
@@ -1425,7 +1422,7 @@ inline void MicroProfileDrawHeader(int32_t nX, uint32_t nWidth, const char* pNam
if(pName)
{
MicroProfileDrawBox(nX-8, MICROPROFILE_TEXT_HEIGHT + 2, nX + nWidth+5, MICROPROFILE_TEXT_HEIGHT + 2 + (MICROPROFILE_TEXT_HEIGHT+1), 0xff000000|g_nMicroProfileBackColors[1]);
- MicroProfileDrawText(nX, MICROPROFILE_TEXT_HEIGHT + 2, (uint32_t)-1, pName, (uint32_t)strlen(pName));
+ MicroProfileDrawText(nX, MICROPROFILE_TEXT_HEIGHT + 2, UINT32_MAX, pName, (uint32_t)strlen(pName));
}
}
@@ -1440,7 +1437,7 @@ inline void MicroProfileLoopActiveGroupsDraw(int32_t nX, int32_t nY, const char*
uint32_t nCount = 0;
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
{
- uint64_t nMask = 1ll << j;
+ uint64_t nMask = 1ULL << j;
if(nMask & nGroup)
{
nY += MICROPROFILE_TEXT_HEIGHT + 1;
@@ -1521,7 +1518,7 @@ inline void MicroProfileCalcTimers(float* pTimers, float* pAverage, float* pMax,
}
}
}
- nMask <<= 1ll;
+ nMask <<= 1;
}
}
@@ -1543,7 +1540,7 @@ inline void MicroProfileDrawBarArrayCallback(uint32_t nTimer, uint32_t nIdx, uin
snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pTimers[nIdx]);
if (!pTimers2)
MicroProfileDrawBox(nX + nTextWidth, nY, nX + nTextWidth + fWidth * pTimers[nIdx+1], nY + nHeight, UI.nOpacityForeground|S.TimerInfo[nTimer].nColor, MicroProfileBoxTypeBar);
- MicroProfileDrawText(nX, nY, (uint32_t)-1, sBuffer, (uint32_t)strlen(sBuffer));
+ MicroProfileDrawText(nX, nY, UINT32_MAX, sBuffer, (uint32_t)strlen(sBuffer));
}
@@ -1564,7 +1561,7 @@ inline void MicroProfileDrawBarCallCountCallback(uint32_t nTimer, uint32_t nIdx,
MicroProfile& S = *MicroProfileGet();
char sBuffer[SBUF_MAX];
int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5d", S.Frame[nTimer].nCount);//fix
- MicroProfileDrawText(nX, nY, (uint32_t)-1, sBuffer, nLen);
+ MicroProfileDrawText(nX, nY, UINT32_MAX, sBuffer, nLen);
}
inline uint32_t MicroProfileDrawBarCallCount(int32_t nX, int32_t nY, const char* pName)
@@ -1588,7 +1585,7 @@ inline void MicroProfileDrawBarMetaAverageCallback(uint32_t nTimer, uint32_t nId
float fRcpFrames = pArgs->fRcpFrames;
char sBuffer[SBUF_MAX];
int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5.2f", pCounters[nTimer] * fRcpFrames);
- MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen);
+ MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, UINT32_MAX, sBuffer, nLen);
}
inline uint32_t MicroProfileDrawBarMetaAverage(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
@@ -1609,8 +1606,8 @@ inline void MicroProfileDrawBarMetaCountCallback(uint32_t nTimer, uint32_t nIdx,
{
uint64_t* pCounters = (uint64_t*)pExtra;
char sBuffer[SBUF_MAX];
- int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5llu", pCounters[nTimer]);
- MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, (uint32_t)-1, sBuffer, nLen);
+ int nLen = snprintf(sBuffer, SBUF_MAX-1, "%5" PRIu64, pCounters[nTimer]);
+ MicroProfileDrawText(nX - nLen * (MICROPROFILE_TEXT_WIDTH+1), nY, UINT32_MAX, sBuffer, nLen);
}
inline uint32_t MicroProfileDrawBarMetaCount(int32_t nX, int32_t nY, uint64_t* pCounters, const char* pName, uint32_t nTotalHeight)
@@ -1667,7 +1664,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
if(bMouseOver)
{
float fXAvg = fMouseXPrc * MICROPROFILE_GRAPH_WIDTH + nX;
- MicroProfileDrawLineVertical(fXAvg, nY, nY + MICROPROFILE_GRAPH_HEIGHT, (uint32_t)-1);
+ MicroProfileDrawLineVertical(fXAvg, nY, nY + MICROPROFILE_GRAPH_HEIGHT, UINT32_MAX);
}
@@ -1706,7 +1703,7 @@ bool MicroProfileDrawGraph(uint32_t nScreenWidth, uint32_t nScreenHeight)
char buf[32];
int nLen = snprintf(buf, sizeof(buf)-1, "%5.2fms", S.fReferenceTime);
- MicroProfileDrawText(nX+1, fY1 - (2+MICROPROFILE_TEXT_HEIGHT), (uint32_t)-1, buf, nLen);
+ MicroProfileDrawText(nX+1, fY1 - (2+MICROPROFILE_TEXT_HEIGHT), UINT32_MAX, buf, nLen);
}
@@ -1782,7 +1779,7 @@ void MicroProfileDumpTimers()
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
{
- uint64_t nMask = 1ll << j;
+ uint64_t nMask = 1ULL << j;
if(nMask & nActiveGroup)
{
MICROPROFILE_PRINTF("%s\n", S.GroupInfo[j].pName);
@@ -1823,7 +1820,7 @@ inline void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeigh
uint32_t nNumGroups = 0;
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
{
- if(nActiveGroup & (1ll << j))
+ if(nActiveGroup & (1ULL << j))
{
nNumTimers += S.GroupInfo[j].nNumTimers;
nNumGroups += 1;
@@ -1878,7 +1875,7 @@ inline void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeigh
for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i)
{
uint32_t nY0 = nY + i * (nHeight + 1);
- bool bInside = (UI.nActiveMenu == -1) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
+ bool bInside = (UI.nActiveMenu == UINT32_MAX) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
MicroProfileDrawBox(nX, nY0, nWidth+nX, nY0 + (nHeight+1)+1, UI.nOpacityBackground | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0)));
}
nX += 10;
@@ -1927,22 +1924,22 @@ inline void MicroProfileDrawBarView(uint32_t nScreenWidth, uint32_t nScreenHeigh
nY = nHeight + 3 - UI.nOffsetY;
for(uint32_t i = 0; i < nNumTimers+nNumGroups+1; ++i)
{
- uint32_t nY0 = nY + i * (nHeight + 1);
- bool bInside = (UI.nActiveMenu == -1) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
+ const uint32_t nY0 = nY + i * (nHeight + 1);
+ const bool bInside = (UI.nActiveMenu == UINT32_MAX) && ((UI.nMouseY >= nY0) && (UI.nMouseY < (nY0 + nHeight + 1)));
MicroProfileDrawBox(nX, nY0, nTimerWidth, nY0 + (nHeight+1)+1, 0xff0000000 | (g_nMicroProfileBackColors[nColorIndex++ & 1] + ((bInside) ? 0x002c2c2c : 0)));
}
nX += MicroProfileDrawBarLegend(nX, nY, nTotalHeight, nTimerWidth-5) + 1;
for(uint32_t j = 0; j < MICROPROFILE_MAX_GROUPS; ++j)
{
- if(nActiveGroup & (1ll << j))
+ if(nActiveGroup & (1ULL << j))
{
- MicroProfileDrawText(nX, nY + (1+nHeight) * nLegendOffset, (uint32_t)-1, S.GroupInfo[j].pName, S.GroupInfo[j].nNameLen);
+ MicroProfileDrawText(nX, nY + (1+nHeight) * nLegendOffset, UINT32_MAX, S.GroupInfo[j].pName, S.GroupInfo[j].nNameLen);
nLegendOffset += S.GroupInfo[j].nNumTimers+1;
}
}
MicroProfileDrawHeader(nX, nTimerWidth-5, "Group");
- MicroProfileDrawTextRight(nTimerWidth-3, MICROPROFILE_TEXT_HEIGHT + 2, (uint32_t)-1, "Timer", 5);
+ MicroProfileDrawTextRight(nTimerWidth-3, MICROPROFILE_TEXT_HEIGHT + 2, UINT32_MAX, "Timer", 5);
MicroProfileDrawLineVertical(nTimerWidth, 0, nTotalHeight+nY, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
MicroProfileDrawLineHorizontal(0, nWidth, 2*MICROPROFILE_TEXT_HEIGHT + 3, UI.nOpacityBackground|g_nMicroProfileBackColors[0]|g_nMicroProfileBackColors[1]);
}
@@ -2003,7 +2000,7 @@ inline const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
}
else
{
- *bSelected = 0 != (S.nActiveGroupWanted & (1ll << Item.nIndex));
+ *bSelected = 0 != (S.nActiveGroupWanted & (1ULL << Item.nIndex));
snprintf(buffer, sizeof(buffer)-1, " %s", Item.pName);
}
return buffer;
@@ -2015,16 +2012,18 @@ inline const char* MicroProfileUIMenuGroups(int nIndex, bool* bSelected)
inline const char* MicroProfileUIMenuAggregate(int nIndex, bool* bSelected)
{
MicroProfile& S = *MicroProfileGet();
- if(nIndex < sizeof(g_MicroProfileAggregatePresets)/sizeof(g_MicroProfileAggregatePresets[0]))
+ if(static_cast<uint32_t>(nIndex) < g_MicroProfileAggregatePresets.size())
{
- int val = g_MicroProfileAggregatePresets[nIndex];
- *bSelected = (int)S.nAggregateFlip == val;
- if(0 == val)
+ uint32_t val = g_MicroProfileAggregatePresets[nIndex];
+ *bSelected = S.nAggregateFlip == val;
+ if (0 == val)
+ {
return "Infinite";
+ }
else
{
static char buf[128];
- snprintf(buf, sizeof(buf)-1, "%7d", val);
+ snprintf(buf, sizeof(buf)-1, "%7u", val);
return buf;
}
}
@@ -2098,11 +2097,13 @@ inline const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected)
{
static char buf[128];
*bSelected = false;
- int nNumPresets = sizeof(g_MicroProfilePresetNames) / sizeof(g_MicroProfilePresetNames[0]);
+ int nNumPresets = static_cast<int>(g_MicroProfilePresetNames.size());
int nIndexSave = nIndex - nNumPresets - 1;
- if(nIndex == nNumPresets)
+ if (nIndex == nNumPresets)
+ {
return "--";
- else if(nIndexSave >=0 && nIndexSave <nNumPresets)
+ }
+ else if(nIndexSave >=0 && nIndexSave < nNumPresets)
{
snprintf(buf, sizeof(buf)-1, "Save '%s'", g_MicroProfilePresetNames[nIndexSave]);
return buf;
@@ -2120,13 +2121,13 @@ inline const char* MicroProfileUIMenuPreset(int nIndex, bool* bSelected)
inline const char* MicroProfileUIMenuCustom(int nIndex, bool* bSelected)
{
- if((uint32_t)-1 == UI.nCustomActive)
+ if(UINT32_MAX == UI.nCustomActive)
{
*bSelected = nIndex == 0;
}
else
{
- *bSelected = nIndex-2 == UI.nCustomActive;
+ *bSelected = nIndex-2 == static_cast<int>(UI.nCustomActive);
}
switch(nIndex)
{
@@ -2202,7 +2203,7 @@ inline void MicroProfileUIClickGroups(int nIndex)
else
{
MP_ASSERT(Item.nIndex < S.nGroupCount);
- S.nActiveGroupWanted ^= (1ll << Item.nIndex);
+ S.nActiveGroupWanted ^= (1ULL << Item.nIndex);
}
}
}
@@ -2273,7 +2274,7 @@ inline void MicroProfileUIClickOptions(int nIndex)
inline void MicroProfileUIClickPreset(int nIndex)
{
- int nNumPresets = sizeof(g_MicroProfilePresetNames) / sizeof(g_MicroProfilePresetNames[0]);
+ int nNumPresets = static_cast<int>(g_MicroProfilePresetNames.size());
int nIndexSave = nIndex - nNumPresets - 1;
if(nIndexSave >= 0 && nIndexSave < nNumPresets)
{
@@ -2310,7 +2311,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
uint32_t nX = 0;
uint32_t nY = 0;
- bool bMouseOver = UI.nMouseY < MICROPROFILE_TEXT_HEIGHT + 1;
+
#define SBUF_SIZE 256
char buffer[256];
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + (MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff000000|g_nMicroProfileBackColors[1]);
@@ -2321,7 +2322,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
uint32_t nNumMenuItems = 0;
int nLen = snprintf(buffer, 127, "MicroProfile");
- MicroProfileDrawText(nX, nY, (uint32_t)-1, buffer, nLen);
+ MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
nX += (sizeof("MicroProfile")+2) * (MICROPROFILE_TEXT_WIDTH+1);
pMenuText[nNumMenuItems++] = "Mode";
pMenuText[nNumMenuItems++] = "Groups";
@@ -2409,7 +2410,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
};
- uint32_t nSelectMenu = (uint32_t)-1;
+ uint32_t nSelectMenu = UINT32_MAX;
for(uint32_t i = 0; i < nNumMenuItems; ++i)
{
nMenuX[i] = nX;
@@ -2419,17 +2420,17 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
{
MicroProfileDrawBox(nX-1, nY, nX + nLen * (MICROPROFILE_TEXT_WIDTH+1), nY +(MICROPROFILE_TEXT_HEIGHT+1)+1, 0xff888888);
nSelectMenu = i;
- if((UI.nMouseLeft || UI.nMouseRight) && i == (int)nPauseIndex)
+ if((UI.nMouseLeft || UI.nMouseRight) && i == (uint32_t)nPauseIndex)
{
S.nToggleRunning = 1;
}
}
- MicroProfileDrawText(nX, nY, (uint32_t)-1, pMenuText[i], (uint32_t)strlen(pMenuText[i]));
+ MicroProfileDrawText(nX, nY, UINT32_MAX, pMenuText[i], (uint32_t)strlen(pMenuText[i]));
nX += (nLen+1) * (MICROPROFILE_TEXT_WIDTH+1);
}
- uint32_t nMenu = nSelectMenu != (uint32_t)-1 ? nSelectMenu : UI.nActiveMenu;
+ uint32_t nMenu = nSelectMenu != UINT32_MAX ? nSelectMenu : UI.nActiveMenu;
UI.nActiveMenu = nMenu;
- if((uint32_t)-1 != nMenu)
+ if(UINT32_MAX != nMenu)
{
nX = nMenuX[nMenu];
nY += MICROPROFILE_TEXT_HEIGHT+1;
@@ -2450,9 +2451,9 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
{
UI.nActiveMenu = nMenu;
}
- else if(nSelectMenu == (uint32_t)-1)
+ else if(nSelectMenu == UINT32_MAX)
{
- UI.nActiveMenu = (uint32_t)-1;
+ UI.nActiveMenu = UINT32_MAX;
}
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + nHeight, 0xff000000|g_nMicroProfileBackColors[1]);
for(int i = 0; i < nNumLines; ++i)
@@ -2461,7 +2462,6 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
const char* pString = CB(i, &bSelected);
if(UI.nMouseY >= nY && UI.nMouseY < nY + MICROPROFILE_TEXT_HEIGHT + 1)
{
- bMouseOver = true;
if(UI.nMouseLeft || UI.nMouseRight)
{
CBClick[nMenu](i);
@@ -2469,7 +2469,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
MicroProfileDrawBox(nX, nY, nX + nWidth, nY + MICROPROFILE_TEXT_HEIGHT + 1, 0xff888888);
}
int nLen = snprintf(buffer, SBUF_SIZE-1, "%c %s", bSelected ? '*' : ' ' ,pString);
- MicroProfileDrawText(nX, nY, (uint32_t)-1, buffer, nLen);
+ MicroProfileDrawText(nX, nY, UINT32_MAX, buffer, nLen);
nY += MICROPROFILE_TEXT_HEIGHT+1;
}
}
@@ -2484,7 +2484,7 @@ inline void MicroProfileDrawMenu(uint32_t nWidth, uint32_t nHeight)
float fMaxMs = fToMs * S.nFlipMaxDisplay;
int nLen = snprintf(FrameTimeMessage, sizeof(FrameTimeMessage)-1, "Time[%6.2f] Avg[%6.2f] Max[%6.2f]", fMs, fAverageMs, fMaxMs);
pMenuText[nNumMenuItems++] = &FrameTimeMessage[0];
- MicroProfileDrawText(nWidth - nLen * (MICROPROFILE_TEXT_WIDTH+1), 0, -1, FrameTimeMessage, nLen);
+ MicroProfileDrawText(nWidth - nLen * (MICROPROFILE_TEXT_WIDTH+1), 0, UINT32_MAX, FrameTimeMessage, nLen);
}
}
@@ -2538,7 +2538,7 @@ inline void MicroProfileMoveGraph()
inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
{
- if((uint32_t)-1 != UI.nCustomActive)
+ if(UINT32_MAX != UI.nCustomActive)
{
MicroProfile& S = *MicroProfileGet();
MP_ASSERT(UI.nCustomActive < MICROPROFILE_CUSTOM_MAX);
@@ -2571,8 +2571,8 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
pColors[i] = S.TimerInfo[nTimerIndex].nColor;
}
- MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 3*MICROPROFILE_TEXT_WIDTH, nOffsetY, (uint32_t)-1, "Avg", sizeof("Avg")-1);
- MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 13*MICROPROFILE_TEXT_WIDTH, nOffsetY, (uint32_t)-1, "Max", sizeof("Max")-1);
+ MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 3*MICROPROFILE_TEXT_WIDTH, nOffsetY, UINT32_MAX, "Avg", sizeof("Avg")-1);
+ MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING + 13*MICROPROFILE_TEXT_WIDTH, nOffsetY, UINT32_MAX, "Max", sizeof("Max")-1);
for(uint32_t i = 0; i < nCount; ++i)
{
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
@@ -2582,10 +2582,10 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
int nSize;
uint32_t nOffsetX = MICROPROFILE_CUSTOM_PADDING;
nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeAvg[i]);
- MicroProfileDrawText(nOffsetX, nOffsetY, (uint32_t)-1, Buffer, nSize);
+ MicroProfileDrawText(nOffsetX, nOffsetY, UINT32_MAX, Buffer, nSize);
nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1);
nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2f", pTimeMax[i]);
- MicroProfileDrawText(nOffsetX, nOffsetY, (uint32_t)-1, Buffer, nSize);
+ MicroProfileDrawText(nOffsetX, nOffsetY, UINT32_MAX, Buffer, nSize);
nOffsetX += (nSize+2) * (MICROPROFILE_TEXT_WIDTH+1);
nSize = snprintf(Buffer, sizeof(Buffer)-1, "%s:%s", S.GroupInfo[nGroupIndex].pName, pTimerInfo->pName);
MicroProfileDrawText(nOffsetX, nOffsetY, pTimerInfo->nColor, Buffer, nSize);
@@ -2599,9 +2599,9 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
nOffsetY = nOffsetYBase;
float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? pTimeMax : pTimeAvg;
const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_BAR_SOURCE_MAX ? "Max" : "Avg";
- MicroProfileDrawText(nMaxOffsetX, nOffsetY, (uint32_t)-1, pString, static_cast<uint32_t>(strlen(pString)));
+ MicroProfileDrawText(nMaxOffsetX, nOffsetY, UINT32_MAX, pString, static_cast<uint32_t>(strlen(pString)));
int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
- MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, (uint32_t)-1, Buffer, nSize);
+ MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, UINT32_MAX, Buffer, nSize);
for(uint32_t i = 0; i < nCount; ++i)
{
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
@@ -2613,9 +2613,9 @@ inline void MicroProfileDrawCustom(uint32_t nWidth, uint32_t nHeight)
{
nOffsetY += 2*(1+MICROPROFILE_TEXT_HEIGHT);
const char* pString = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? "Max" : "Avg";
- MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING, nOffsetY, (uint32_t)-1, pString, static_cast<uint32_t>(strlen(pString)));
+ MicroProfileDrawText(MICROPROFILE_CUSTOM_PADDING, nOffsetY, UINT32_MAX, pString, static_cast<uint32_t>(strlen(pString)));
int nSize = snprintf(Buffer, sizeof(Buffer)-1, "%6.2fms", fReference);
- MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, (uint32_t)-1, Buffer, nSize);
+ MicroProfileDrawText(nReducedWidth - (1+nSize) * (MICROPROFILE_TEXT_WIDTH+1), nOffsetY, UINT32_MAX, Buffer, nSize);
nOffsetY += (1+MICROPROFILE_TEXT_HEIGHT);
float fPosX = MICROPROFILE_CUSTOM_PADDING;
float* pMs = pCustom->nFlags & MICROPROFILE_CUSTOM_STACK_SOURCE_MAX ? pTimeMax : pTimeAvg;
@@ -2668,7 +2668,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
UI.nHoverTime = 0;
UI.nHoverFrame = -1;
if(S.nDisplay != MP_DRAW_DETAILED)
- S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = -1;
+ S.nContextSwitchHoverThread = S.nContextSwitchHoverThreadAfter = S.nContextSwitchHoverThreadBefore = UINT32_MAX;
MicroProfileMoveGraph();
@@ -2798,13 +2798,13 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
- if(UI.nActiveMenu == -1 && !bMouseOverGraph)
+ if(UI.nActiveMenu == UINT32_MAX && !bMouseOverGraph)
{
if(UI.nHoverToken != MICROPROFILE_INVALID_TOKEN)
{
MicroProfileDrawFloatTooltip(UI.nMouseX, UI.nMouseY, UI.nHoverToken, UI.nHoverTime);
}
- else if(S.nContextSwitchHoverThreadAfter != -1 && S.nContextSwitchHoverThreadBefore != -1)
+ else if(S.nContextSwitchHoverThreadAfter != UINT32_MAX && S.nContextSwitchHoverThreadBefore != UINT32_MAX)
{
float fToMs = MicroProfileTickToMsMultiplier(MicroProfileTicksPerSecondCpu());
MicroProfileStringArray ToolTip;
@@ -2820,7 +2820,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
MicroProfileStringArrayFormat(&ToolTip, "%6.2fms", fToMs * nDifference );
MicroProfileStringArrayAddLiteral(&ToolTip, "CPU");
MicroProfileStringArrayFormat(&ToolTip, "%d", S.nContextSwitchHoverCpu);
- MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, -1);
+ MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, UINT32_MAX);
}
@@ -2858,7 +2858,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
}
}
#endif
- MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, -1);
+ MicroProfileDrawFloatWindow(UI.nMouseX, UI.nMouseY+20, &ToolTip.ppStrings[0], ToolTip.nNumStrings, UINT32_MAX);
}
if(UI.nMouseLeft)
{
@@ -2883,7 +2883,7 @@ inline void MicroProfileDraw(uint32_t nWidth, uint32_t nHeight)
#endif
m.unlock();
}
- else if(UI.nCustomActive != (uint32_t)-1)
+ else if(UI.nCustomActive != UINT32_MAX)
{
std::recursive_mutex& m = MicroProfileGetMutex();
m.lock();
@@ -3179,7 +3179,7 @@ void MicroProfileLoadPreset(const char* pSuffix)
{
if(0 == MP_STRCASECMP(pGroupName, S.GroupInfo[j].pName))
{
- S.nActiveGroupWanted |= (1ll << j);
+ S.nActiveGroupWanted |= (1ULL << j);
}
}
}
@@ -3212,7 +3212,7 @@ void MicroProfileLoadPreset(const char* pSuffix)
uint64_t nGroupIndex = S.TimerInfo[j].nGroupIndex;
if(0 == MP_STRCASECMP(pGraphName, S.TimerInfo[j].pName) && 0 == MP_STRCASECMP(pGraphGroupName, S.GroupInfo[nGroupIndex].pName))
{
- MicroProfileToken nToken = MicroProfileMakeToken(1ll << nGroupIndex, (uint16_t)j);
+ MicroProfileToken nToken = MicroProfileMakeToken(1ULL << nGroupIndex, (uint16_t)j);
S.Graph[i].nToken = nToken; // note: group index is stored here but is checked without in MicroProfileToggleGraph()!
S.TimerInfo[j].bGraph = true;
if(nToken != nPrevToken)
@@ -3235,7 +3235,7 @@ inline uint32_t MicroProfileCustomGroupFind(const char* pCustomName)
return i;
}
}
- return (uint32_t)-1;
+ return UINT32_MAX;
}
inline uint32_t MicroProfileCustomGroup(const char* pCustomName)
@@ -3251,7 +3251,7 @@ inline uint32_t MicroProfileCustomGroup(const char* pCustomName)
uint32_t nIndex = UI.nCustomCount;
UI.nCustomCount++;
memset(&UI.Custom[nIndex], 0, sizeof(UI.Custom[nIndex]));
- uint32_t nLen = (uint32_t)strlen(pCustomName);
+ size_t nLen = strlen(pCustomName);
if(nLen > MICROPROFILE_NAME_MAX_LEN-1)
nLen = MICROPROFILE_NAME_MAX_LEN-1;
memcpy(&UI.Custom[nIndex].pName[0], pCustomName, nLen);
@@ -3309,7 +3309,7 @@ inline void MicroProfileCustomGroupEnable(uint32_t nIndex)
void MicroProfileCustomGroupToggle(const char* pCustomName)
{
uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName);
- if(nIndex == (uint32_t)-1 || nIndex == UI.nCustomActive)
+ if(nIndex == UINT32_MAX || nIndex == UI.nCustomActive)
{
MicroProfileCustomGroupDisable();
}
@@ -3328,13 +3328,13 @@ void MicroProfileCustomGroupDisable()
{
MicroProfile& S = *MicroProfileGet();
S.nForceGroupUI = 0;
- UI.nCustomActive = (uint32_t)-1;
+ UI.nCustomActive = UINT32_MAX;
}
void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup, const char* pTimer)
{
uint32_t nIndex = MicroProfileCustomGroupFind(pCustomName);
- if((uint32_t)-1 == nIndex)
+ if(UINT32_MAX == nIndex)
{
return;
}
@@ -3344,7 +3344,7 @@ void MicroProfileCustomGroupAddTimer(const char* pCustomName, const char* pGroup
MP_ASSERT(nToken != MICROPROFILE_INVALID_TOKEN); //Timer must be registered first.
UI.Custom[nIndex].pTimers[nTimerIndex] = nToken;
uint16_t nGroup = MicroProfileGetGroupIndex(nToken);
- UI.Custom[nIndex].nGroupMask |= (1ll << nGroup);
+ UI.Custom[nIndex].nGroupMask |= (1ULL << nGroup);
UI.Custom[nIndex].nNumTimers++;
}
diff --git a/externals/unicorn b/externals/unicorn
deleted file mode 160000
-Subproject 73f45735354396766a4bfb26d0b96b06e5cf31b
diff --git a/externals/xbyak b/externals/xbyak
-Subproject 82b70e665918efc2ee348091742fd0237b3b68c
+Subproject c306b8e5786eeeb87b8925a8af5c3bf057ff5a9
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
index 71efbb40d..dbda528ce 100644
--- a/src/CMakeLists.txt
+++ b/src/CMakeLists.txt
@@ -32,7 +32,6 @@ if (MSVC)
# /Zc:inline - Let codegen omit inline functions in object files
# /Zc:throwingNew - Let codegen assume `operator new` (without std::nothrow) will never return null
add_compile_options(
- /W3
/MP
/Zi
/Zo
@@ -43,6 +42,13 @@ if (MSVC)
/Zc:externConstexpr
/Zc:inline
/Zc:throwingNew
+
+ # Warnings
+ /W3
+ /we4547 # 'operator' : operator before comma has no effect; expected operator with side-effect
+ /we4549 # 'operator1': operator before comma has no effect; did you intend 'operator2'?
+ /we4555 # Expression has no effect; expected expression with side-effect
+ /we4834 # Discarding return value of function with 'nodiscard' attribute
)
# /GS- - No stack buffer overflow checks
@@ -56,6 +62,7 @@ else()
-Werror=implicit-fallthrough
-Werror=missing-declarations
-Werror=reorder
+ -Werror=unused-result
-Wextra
-Wmissing-declarations
-Wno-attributes
diff --git a/src/audio_core/CMakeLists.txt b/src/audio_core/CMakeLists.txt
index 5ef38a337..68c67507b 100644
--- a/src/audio_core/CMakeLists.txt
+++ b/src/audio_core/CMakeLists.txt
@@ -12,22 +12,54 @@ add_library(audio_core STATIC
buffer.h
codec.cpp
codec.h
+ command_generator.cpp
+ command_generator.h
common.h
+ effect_context.cpp
+ effect_context.h
+ info_updater.cpp
+ info_updater.h
+ memory_pool.cpp
+ memory_pool.h
+ mix_context.cpp
+ mix_context.h
null_sink.h
sink.h
+ sink_context.cpp
+ sink_context.h
sink_details.cpp
sink_details.h
sink_stream.h
+ splitter_context.cpp
+ splitter_context.h
stream.cpp
stream.h
time_stretch.cpp
time_stretch.h
+ voice_context.cpp
+ voice_context.h
$<$<BOOL:${ENABLE_CUBEB}>:cubeb_sink.cpp cubeb_sink.h>
)
create_target_directory_groups(audio_core)
+if (NOT MSVC)
+ target_compile_options(audio_core PRIVATE
+ -Werror=conversion
+ -Werror=ignored-qualifiers
+ -Werror=implicit-fallthrough
+ -Werror=reorder
+ -Werror=sign-compare
+ -Werror=unused-variable
+
+ $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
+ $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
+
+ -Wno-sign-conversion
+ )
+endif()
+
target_link_libraries(audio_core PUBLIC common core)
target_link_libraries(audio_core PRIVATE SoundTouch)
diff --git a/src/audio_core/algorithm/filter.cpp b/src/audio_core/algorithm/filter.cpp
index f65bf64f7..f34a5b9f3 100644
--- a/src/audio_core/algorithm/filter.cpp
+++ b/src/audio_core/algorithm/filter.cpp
@@ -55,7 +55,8 @@ void Filter::Process(std::vector<s16>& signal) {
/// @param total_count The total number of biquads to be cascaded.
/// @param index 0-index of the biquad to calculate the Q value for.
static double CascadingBiquadQ(std::size_t total_count, std::size_t index) {
- const double pole = M_PI * (2 * index + 1) / (4.0 * total_count);
+ const auto pole =
+ M_PI * static_cast<double>(2 * index + 1) / (4.0 * static_cast<double>(total_count));
return 1.0 / (2.0 * std::cos(pole));
}
diff --git a/src/audio_core/algorithm/interpolate.cpp b/src/audio_core/algorithm/interpolate.cpp
index 49ab9d3e1..699fcb84c 100644
--- a/src/audio_core/algorithm/interpolate.cpp
+++ b/src/audio_core/algorithm/interpolate.cpp
@@ -146,7 +146,7 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
return {};
if (ratio <= 0) {
- LOG_CRITICAL(Audio, "Nonsensical interpolation ratio {}", ratio);
+ LOG_ERROR(Audio, "Nonsensical interpolation ratio {}", ratio);
return input;
}
@@ -164,7 +164,8 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
const std::size_t num_frames{input.size() / 2};
std::vector<s16> output;
- output.reserve(static_cast<std::size_t>(input.size() / ratio + InterpolationState::taps));
+ output.reserve(static_cast<std::size_t>(static_cast<double>(input.size()) / ratio +
+ InterpolationState::taps));
for (std::size_t frame{}; frame < num_frames; ++frame) {
const std::size_t lut_index{(state.fraction >> 8) * InterpolationState::taps};
@@ -197,4 +198,36 @@ std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16> input,
return output;
}
+void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count) {
+ const std::array<s16, 512>& lut = [pitch] {
+ if (pitch > 0xaaaa) {
+ return curve_lut0;
+ }
+ if (pitch <= 0x8000) {
+ return curve_lut1;
+ }
+ return curve_lut2;
+ }();
+
+ std::size_t index{};
+
+ for (std::size_t i = 0; i < sample_count; i++) {
+ const std::size_t lut_index{(static_cast<std::size_t>(fraction) >> 8) * 4};
+ const auto l0 = lut[lut_index + 0];
+ const auto l1 = lut[lut_index + 1];
+ const auto l2 = lut[lut_index + 2];
+ const auto l3 = lut[lut_index + 3];
+
+ const auto s0 = static_cast<s32>(input[index]);
+ const auto s1 = static_cast<s32>(input[index + 1]);
+ const auto s2 = static_cast<s32>(input[index + 2]);
+ const auto s3 = static_cast<s32>(input[index + 3]);
+
+ output[i] = (l0 * s0 + l1 * s1 + l2 * s2 + l3 * s3) >> 15;
+ fraction += pitch;
+ index += (fraction >> 15);
+ fraction &= 0x7fff;
+ }
+}
+
} // namespace AudioCore
diff --git a/src/audio_core/algorithm/interpolate.h b/src/audio_core/algorithm/interpolate.h
index ab1a31754..d534077af 100644
--- a/src/audio_core/algorithm/interpolate.h
+++ b/src/audio_core/algorithm/interpolate.h
@@ -38,4 +38,7 @@ inline std::vector<s16> Interpolate(InterpolationState& state, std::vector<s16>
return Interpolate(state, std::move(input), ratio);
}
+/// Nintendo Switchs DSP resampling algorithm. Based on a single channel
+void Resample(s32* output, const s32* input, s32 pitch, s32& fraction, std::size_t sample_count);
+
} // namespace AudioCore
diff --git a/src/audio_core/audio_renderer.cpp b/src/audio_core/audio_renderer.cpp
index d64452617..a7e851bb8 100644
--- a/src/audio_core/audio_renderer.cpp
+++ b/src/audio_core/audio_renderer.cpp
@@ -2,95 +2,46 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "audio_core/algorithm/interpolate.h"
+#include <vector>
+
#include "audio_core/audio_out.h"
#include "audio_core/audio_renderer.h"
-#include "audio_core/codec.h"
#include "audio_core/common.h"
-#include "common/assert.h"
+#include "audio_core/info_updater.h"
+#include "audio_core/voice_context.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/hle/kernel/writable_event.h"
#include "core/memory.h"
+#include "core/settings.h"
namespace AudioCore {
-
-constexpr u32 STREAM_SAMPLE_RATE{48000};
-constexpr u32 STREAM_NUM_CHANNELS{2};
-using VoiceChannelHolder = std::array<VoiceResourceInformation*, 6>;
-class AudioRenderer::VoiceState {
-public:
- bool IsPlaying() const {
- return is_in_use && info.play_state == PlayState::Started;
- }
-
- const VoiceOutStatus& GetOutStatus() const {
- return out_status;
- }
-
- const VoiceInfo& GetInfo() const {
- return info;
- }
-
- VoiceInfo& GetInfo() {
- return info;
- }
-
- void SetWaveIndex(std::size_t index);
- std::vector<s16> DequeueSamples(std::size_t sample_count, Core::Memory::Memory& memory,
- const VoiceChannelHolder& voice_resources);
- void UpdateState();
- void RefreshBuffer(Core::Memory::Memory& memory, const VoiceChannelHolder& voice_resources);
-
-private:
- bool is_in_use{};
- bool is_refresh_pending{};
- std::size_t wave_index{};
- std::size_t offset{};
- Codec::ADPCMState adpcm_state{};
- InterpolationState interp_state{};
- std::vector<s16> samples;
- VoiceOutStatus out_status{};
- VoiceInfo info{};
-};
-
-class AudioRenderer::EffectState {
-public:
- const EffectOutStatus& GetOutStatus() const {
- return out_status;
- }
-
- const EffectInStatus& GetInfo() const {
- return info;
- }
-
- EffectInStatus& GetInfo() {
- return info;
- }
-
- void UpdateState(Core::Memory::Memory& memory);
-
-private:
- EffectOutStatus out_status{};
- EffectInStatus info{};
-};
-
AudioRenderer::AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
- AudioRendererParameter params,
+ AudioCommon::AudioRendererParameter params,
std::shared_ptr<Kernel::WritableEvent> buffer_event,
std::size_t instance_number)
- : worker_params{params}, buffer_event{buffer_event}, voices(params.voice_count),
- voice_resources(params.voice_count), effects(params.effect_count), memory{memory_} {
+ : worker_params{params}, buffer_event{buffer_event},
+ memory_pool_info(params.effect_count + params.voice_count * 4),
+ voice_context(params.voice_count), effect_context(params.effect_count), mix_context(),
+ sink_context(params.sink_count), splitter_context(),
+ voices(params.voice_count), memory{memory_},
+ command_generator(worker_params, voice_context, mix_context, splitter_context, effect_context,
+ memory),
+ temp_mix_buffer(AudioCommon::TOTAL_TEMP_MIX_SIZE) {
behavior_info.SetUserRevision(params.revision);
+ splitter_context.Initialize(behavior_info, params.splitter_count,
+ params.num_splitter_send_channels);
+ mix_context.Initialize(behavior_info, params.submix_count + 1, params.effect_count);
audio_out = std::make_unique<AudioCore::AudioOut>();
- stream = audio_out->OpenStream(core_timing, STREAM_SAMPLE_RATE, STREAM_NUM_CHANNELS,
- fmt::format("AudioRenderer-Instance{}", instance_number),
- [=]() { buffer_event->Signal(); });
+ stream =
+ audio_out->OpenStream(core_timing, params.sample_rate, AudioCommon::STREAM_NUM_CHANNELS,
+ fmt::format("AudioRenderer-Instance{}", instance_number),
+ [=]() { buffer_event->Signal(); });
audio_out->StartStream(stream);
QueueMixedBuffer(0);
QueueMixedBuffer(1);
QueueMixedBuffer(2);
+ QueueMixedBuffer(3);
}
AudioRenderer::~AudioRenderer() = default;
@@ -111,355 +62,200 @@ Stream::State AudioRenderer::GetStreamState() const {
return stream->GetState();
}
-ResultVal<std::vector<u8>> AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params) {
- // Copy UpdateDataHeader struct
- UpdateDataHeader config{};
- std::memcpy(&config, input_params.data(), sizeof(UpdateDataHeader));
- u32 memory_pool_count = worker_params.effect_count + (worker_params.voice_count * 4);
-
- if (!behavior_info.UpdateInput(input_params, sizeof(UpdateDataHeader))) {
- LOG_ERROR(Audio, "Failed to update behavior info input parameters");
- return Audren::ERR_INVALID_PARAMETERS;
- }
-
- // Copy MemoryPoolInfo structs
- std::vector<MemoryPoolInfo> mem_pool_info(memory_pool_count);
- std::memcpy(mem_pool_info.data(),
- input_params.data() + sizeof(UpdateDataHeader) + config.behavior_size,
- memory_pool_count * sizeof(MemoryPoolInfo));
-
- // Copy voice resources
- const std::size_t voice_resource_offset{sizeof(UpdateDataHeader) + config.behavior_size +
- config.memory_pools_size};
- std::memcpy(voice_resources.data(), input_params.data() + voice_resource_offset,
- sizeof(VoiceResourceInformation) * voice_resources.size());
-
- // Copy VoiceInfo structs
- std::size_t voice_offset{sizeof(UpdateDataHeader) + config.behavior_size +
- config.memory_pools_size + config.voice_resource_size};
- for (auto& voice : voices) {
- std::memcpy(&voice.GetInfo(), input_params.data() + voice_offset, sizeof(VoiceInfo));
- voice_offset += sizeof(VoiceInfo);
- }
-
- std::size_t effect_offset{sizeof(UpdateDataHeader) + config.behavior_size +
- config.memory_pools_size + config.voice_resource_size +
- config.voices_size};
- for (auto& effect : effects) {
- std::memcpy(&effect.GetInfo(), input_params.data() + effect_offset, sizeof(EffectInStatus));
- effect_offset += sizeof(EffectInStatus);
- }
-
- // Update memory pool state
- std::vector<MemoryPoolEntry> memory_pool(memory_pool_count);
- for (std::size_t index = 0; index < memory_pool.size(); ++index) {
- if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestAttach) {
- memory_pool[index].state = MemoryPoolStates::Attached;
- } else if (mem_pool_info[index].pool_state == MemoryPoolStates::RequestDetach) {
- memory_pool[index].state = MemoryPoolStates::Detached;
- }
- }
-
- // Update voices
- for (auto& voice : voices) {
- voice.UpdateState();
- if (!voice.GetInfo().is_in_use) {
- continue;
- }
- if (voice.GetInfo().is_new) {
- voice.SetWaveIndex(voice.GetInfo().wave_buffer_head);
- }
- }
-
- for (auto& effect : effects) {
- effect.UpdateState(memory);
- }
-
- // Release previous buffers and queue next ones for playback
- ReleaseAndQueueBuffers();
-
- // Copy output header
- UpdateDataHeader response_data{worker_params};
- if (behavior_info.IsElapsedFrameCountSupported()) {
- response_data.render_info = sizeof(RendererInfo);
- response_data.total_size += sizeof(RendererInfo);
- }
-
- std::vector<u8> output_params(response_data.total_size);
- std::memcpy(output_params.data(), &response_data, sizeof(UpdateDataHeader));
-
- // Copy output memory pool entries
- std::memcpy(output_params.data() + sizeof(UpdateDataHeader), memory_pool.data(),
- response_data.memory_pools_size);
-
- // Copy output voice status
- std::size_t voice_out_status_offset{sizeof(UpdateDataHeader) + response_data.memory_pools_size};
- for (const auto& voice : voices) {
- std::memcpy(output_params.data() + voice_out_status_offset, &voice.GetOutStatus(),
- sizeof(VoiceOutStatus));
- voice_out_status_offset += sizeof(VoiceOutStatus);
- }
+static constexpr s16 ClampToS16(s32 value) {
+ return static_cast<s16>(std::clamp(value, -32768, 32767));
+}
- std::size_t effect_out_status_offset{
- sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
- response_data.voice_resource_size};
- for (const auto& effect : effects) {
- std::memcpy(output_params.data() + effect_out_status_offset, &effect.GetOutStatus(),
- sizeof(EffectOutStatus));
- effect_out_status_offset += sizeof(EffectOutStatus);
- }
+ResultCode AudioRenderer::UpdateAudioRenderer(const std::vector<u8>& input_params,
+ std::vector<u8>& output_params) {
- // Update behavior info output
- const std::size_t behavior_out_status_offset{
- sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
- response_data.effects_size + response_data.sinks_size +
- response_data.performance_manager_size};
+ InfoUpdater info_updater{input_params, output_params, behavior_info};
- if (!behavior_info.UpdateOutput(output_params, behavior_out_status_offset)) {
- LOG_ERROR(Audio, "Failed to update behavior info output parameters");
- return Audren::ERR_INVALID_PARAMETERS;
+ if (!info_updater.UpdateBehaviorInfo(behavior_info)) {
+ LOG_ERROR(Audio, "Failed to update behavior info input parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- if (behavior_info.IsElapsedFrameCountSupported()) {
- const std::size_t renderer_info_offset{
- sizeof(UpdateDataHeader) + response_data.memory_pools_size + response_data.voices_size +
- response_data.effects_size + response_data.sinks_size +
- response_data.performance_manager_size + response_data.behavior_size};
- RendererInfo renderer_info{};
- renderer_info.elasped_frame_count = elapsed_frame_count;
- std::memcpy(output_params.data() + renderer_info_offset, &renderer_info,
- sizeof(RendererInfo));
+ if (!info_updater.UpdateMemoryPools(memory_pool_info)) {
+ LOG_ERROR(Audio, "Failed to update memory pool parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- return MakeResult(output_params);
-}
-
-void AudioRenderer::VoiceState::SetWaveIndex(std::size_t index) {
- wave_index = index & 3;
- is_refresh_pending = true;
-}
-
-std::vector<s16> AudioRenderer::VoiceState::DequeueSamples(
- std::size_t sample_count, Core::Memory::Memory& memory,
- const VoiceChannelHolder& voice_resources) {
- if (!IsPlaying()) {
- return {};
+ if (!info_updater.UpdateVoiceChannelResources(voice_context)) {
+ LOG_ERROR(Audio, "Failed to update voice channel resource parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- if (is_refresh_pending) {
- RefreshBuffer(memory, voice_resources);
+ if (!info_updater.UpdateVoices(voice_context, memory_pool_info, 0)) {
+ LOG_ERROR(Audio, "Failed to update voice parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- const std::size_t max_size{samples.size() - offset};
- const std::size_t dequeue_offset{offset};
- std::size_t size{sample_count * STREAM_NUM_CHANNELS};
- if (size > max_size) {
- size = max_size;
+ // TODO(ogniK): Deal with stopped audio renderer but updates still taking place
+ if (!info_updater.UpdateEffects(effect_context, true)) {
+ LOG_ERROR(Audio, "Failed to update effect parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- out_status.played_sample_count += size / STREAM_NUM_CHANNELS;
- offset += size;
-
- const auto& wave_buffer{info.wave_buffer[wave_index]};
- if (offset == samples.size()) {
- offset = 0;
-
- if (!wave_buffer.is_looping && wave_buffer.buffer_sz) {
- SetWaveIndex(wave_index + 1);
- }
-
- if (wave_buffer.buffer_sz) {
- out_status.wave_buffer_consumed++;
- }
-
- if (wave_buffer.end_of_stream || wave_buffer.buffer_sz == 0) {
- info.play_state = PlayState::Paused;
+ if (behavior_info.IsSplitterSupported()) {
+ if (!info_updater.UpdateSplitterInfo(splitter_context)) {
+ LOG_ERROR(Audio, "Failed to update splitter parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
}
- return {samples.begin() + dequeue_offset, samples.begin() + dequeue_offset + size};
-}
+ auto mix_result = info_updater.UpdateMixes(mix_context, worker_params.mix_buffer_count,
+ splitter_context, effect_context);
-void AudioRenderer::VoiceState::UpdateState() {
- if (is_in_use && !info.is_in_use) {
- // No longer in use, reset state
- is_refresh_pending = true;
- wave_index = 0;
- offset = 0;
- out_status = {};
+ if (mix_result.IsError()) {
+ LOG_ERROR(Audio, "Failed to update mix parameters");
+ return mix_result;
}
- is_in_use = info.is_in_use;
-}
-void AudioRenderer::VoiceState::RefreshBuffer(Core::Memory::Memory& memory,
- const VoiceChannelHolder& voice_resources) {
- const auto wave_buffer_address = info.wave_buffer[wave_index].buffer_addr;
- const auto wave_buffer_size = info.wave_buffer[wave_index].buffer_sz;
- std::vector<s16> new_samples(wave_buffer_size / sizeof(s16));
- memory.ReadBlock(wave_buffer_address, new_samples.data(), wave_buffer_size);
-
- switch (static_cast<Codec::PcmFormat>(info.sample_format)) {
- case Codec::PcmFormat::Int16: {
- // PCM16 is played as-is
- break;
+ // TODO(ogniK): Sinks
+ if (!info_updater.UpdateSinks(sink_context)) {
+ LOG_ERROR(Audio, "Failed to update sink parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- case Codec::PcmFormat::Adpcm: {
- // Decode ADPCM to PCM16
- Codec::ADPCM_Coeff coeffs;
- memory.ReadBlock(info.additional_params_addr, coeffs.data(), sizeof(Codec::ADPCM_Coeff));
- new_samples = Codec::DecodeADPCM(reinterpret_cast<u8*>(new_samples.data()),
- new_samples.size() * sizeof(s16), coeffs, adpcm_state);
- break;
- }
- default:
- UNIMPLEMENTED_MSG("Unimplemented sample_format={}", info.sample_format);
- break;
- }
-
- switch (info.channel_count) {
- case 1: {
- // 1 channel is upsampled to 2 channel
- samples.resize(new_samples.size() * 2);
- for (std::size_t index = 0; index < new_samples.size(); ++index) {
- auto sample = static_cast<float>(new_samples[index]);
- if (voice_resources[0]->in_use) {
- sample *= voice_resources[0]->mix_volumes[0];
- }
-
- samples[index * 2] = static_cast<s16>(sample * info.volume);
- samples[index * 2 + 1] = static_cast<s16>(sample * info.volume);
- }
- break;
+ // TODO(ogniK): Performance buffer
+ if (!info_updater.UpdatePerformanceBuffer()) {
+ LOG_ERROR(Audio, "Failed to update performance buffer parameters");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- case 2: {
- // 2 channel is played as is
- samples = std::move(new_samples);
- const std::size_t sample_count = (samples.size() / 2);
- for (std::size_t index = 0; index < sample_count; ++index) {
- const std::size_t index_l = index * 2;
- const std::size_t index_r = index * 2 + 1;
-
- auto sample_l = static_cast<float>(samples[index_l]);
- auto sample_r = static_cast<float>(samples[index_r]);
-
- if (voice_resources[0]->in_use) {
- sample_l *= voice_resources[0]->mix_volumes[0];
- }
-
- if (voice_resources[1]->in_use) {
- sample_r *= voice_resources[1]->mix_volumes[1];
- }
- samples[index_l] = static_cast<s16>(sample_l * info.volume);
- samples[index_r] = static_cast<s16>(sample_r * info.volume);
- }
- break;
+ if (!info_updater.UpdateErrorInfo(behavior_info)) {
+ LOG_ERROR(Audio, "Failed to update error info");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- case 6: {
- samples.resize((new_samples.size() / 6) * 2);
- const std::size_t sample_count = samples.size() / 2;
-
- for (std::size_t index = 0; index < sample_count; ++index) {
- auto FL = static_cast<float>(new_samples[index * 6]);
- auto FR = static_cast<float>(new_samples[index * 6 + 1]);
- auto FC = static_cast<float>(new_samples[index * 6 + 2]);
- auto BL = static_cast<float>(new_samples[index * 6 + 4]);
- auto BR = static_cast<float>(new_samples[index * 6 + 5]);
-
- if (voice_resources[0]->in_use) {
- FL *= voice_resources[0]->mix_volumes[0];
- }
- if (voice_resources[1]->in_use) {
- FR *= voice_resources[1]->mix_volumes[1];
- }
- if (voice_resources[2]->in_use) {
- FC *= voice_resources[2]->mix_volumes[2];
- }
- if (voice_resources[4]->in_use) {
- BL *= voice_resources[4]->mix_volumes[4];
- }
- if (voice_resources[5]->in_use) {
- BR *= voice_resources[5]->mix_volumes[5];
- }
- samples[index * 2] =
- static_cast<s16>((0.3694f * FL + 0.2612f * FC + 0.3694f * BL) * info.volume);
- samples[index * 2 + 1] =
- static_cast<s16>((0.3694f * FR + 0.2612f * FC + 0.3694f * BR) * info.volume);
+ if (behavior_info.IsElapsedFrameCountSupported()) {
+ if (!info_updater.UpdateRendererInfo(elapsed_frame_count)) {
+ LOG_ERROR(Audio, "Failed to update renderer info");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- break;
- }
- default:
- UNIMPLEMENTED_MSG("Unimplemented channel_count={}", info.channel_count);
- break;
}
+ // TODO(ogniK): Statistics
- // Only interpolate when necessary, expensive.
- if (GetInfo().sample_rate != STREAM_SAMPLE_RATE) {
- samples = Interpolate(interp_state, std::move(samples), GetInfo().sample_rate,
- STREAM_SAMPLE_RATE);
+ if (!info_updater.WriteOutputHeader()) {
+ LOG_ERROR(Audio, "Failed to write output header");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
- is_refresh_pending = false;
-}
+ // TODO(ogniK): Check when all sections are implemented
-void AudioRenderer::EffectState::UpdateState(Core::Memory::Memory& memory) {
- if (info.is_new) {
- out_status.state = EffectStatus::New;
- } else {
- if (info.type == Effect::Aux) {
- ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_info) == 0,
- "Aux buffers tried to update");
- ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_info) == 0,
- "Aux buffers tried to update");
- ASSERT_MSG(memory.Read32(info.aux_info.return_buffer_base) == 0,
- "Aux buffers tried to update");
- ASSERT_MSG(memory.Read32(info.aux_info.send_buffer_base) == 0,
- "Aux buffers tried to update");
- }
+ if (!info_updater.CheckConsumedSize()) {
+ LOG_ERROR(Audio, "Audio buffers were not consumed!");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
}
-}
-static constexpr s16 ClampToS16(s32 value) {
- return static_cast<s16>(std::clamp(value, -32768, 32767));
+ ReleaseAndQueueBuffers();
+
+ return RESULT_SUCCESS;
}
void AudioRenderer::QueueMixedBuffer(Buffer::Tag tag) {
- constexpr std::size_t BUFFER_SIZE{512};
+ command_generator.PreCommand();
+ // Clear mix buffers before our next operation
+ command_generator.ClearMixBuffers();
+
+ // If the splitter is not in use, sort our mixes
+ if (!splitter_context.UsingSplitter()) {
+ mix_context.SortInfo();
+ }
+ // Sort our voices
+ voice_context.SortInfo();
+
+ // Handle samples
+ command_generator.GenerateVoiceCommands();
+ command_generator.GenerateSubMixCommands();
+ command_generator.GenerateFinalMixCommands();
+
+ command_generator.PostCommand();
+ // Base sample size
+ std::size_t BUFFER_SIZE{worker_params.sample_count};
+ // Samples
std::vector<s16> buffer(BUFFER_SIZE * stream->GetNumChannels());
-
- for (auto& voice : voices) {
- if (!voice.IsPlaying()) {
- continue;
- }
- VoiceChannelHolder resources{};
- for (u32 channel = 0; channel < voice.GetInfo().channel_count; channel++) {
- const auto channel_resource_id = voice.GetInfo().voice_channel_resource_ids[channel];
- resources[channel] = &voice_resources[channel_resource_id];
+ // Make sure to clear our samples
+ std::memset(buffer.data(), 0, buffer.size() * sizeof(s16));
+
+ if (sink_context.InUse()) {
+ const auto stream_channel_count = stream->GetNumChannels();
+ const auto buffer_offsets = sink_context.OutputBuffers();
+ const auto channel_count = buffer_offsets.size();
+ const auto& final_mix = mix_context.GetFinalMixInfo();
+ const auto& in_params = final_mix.GetInParams();
+ std::vector<s32*> mix_buffers(channel_count);
+ for (std::size_t i = 0; i < channel_count; i++) {
+ mix_buffers[i] =
+ command_generator.GetMixBuffer(in_params.buffer_offset + buffer_offsets[i]);
}
- std::size_t offset{};
- s64 samples_remaining{BUFFER_SIZE};
- while (samples_remaining > 0) {
- const std::vector<s16> samples{
- voice.DequeueSamples(samples_remaining, memory, resources)};
-
- if (samples.empty()) {
- break;
- }
-
- samples_remaining -= samples.size() / stream->GetNumChannels();
-
- for (const auto& sample : samples) {
- const s32 buffer_sample{buffer[offset]};
- buffer[offset++] =
- ClampToS16(buffer_sample + static_cast<s32>(sample * voice.GetInfo().volume));
+ for (std::size_t i = 0; i < BUFFER_SIZE; i++) {
+ if (channel_count == 1) {
+ const auto sample = ClampToS16(mix_buffers[0][i]);
+ buffer[i * stream_channel_count + 0] = sample;
+ if (stream_channel_count > 1) {
+ buffer[i * stream_channel_count + 1] = sample;
+ }
+ if (stream_channel_count == 6) {
+ buffer[i * stream_channel_count + 2] = sample;
+ buffer[i * stream_channel_count + 4] = sample;
+ buffer[i * stream_channel_count + 5] = sample;
+ }
+ } else if (channel_count == 2) {
+ const auto l_sample = ClampToS16(mix_buffers[0][i]);
+ const auto r_sample = ClampToS16(mix_buffers[1][i]);
+ if (stream_channel_count == 1) {
+ buffer[i * stream_channel_count + 0] = l_sample;
+ } else if (stream_channel_count == 2) {
+ buffer[i * stream_channel_count + 0] = l_sample;
+ buffer[i * stream_channel_count + 1] = r_sample;
+ } else if (stream_channel_count == 6) {
+ buffer[i * stream_channel_count + 0] = l_sample;
+ buffer[i * stream_channel_count + 1] = r_sample;
+
+ buffer[i * stream_channel_count + 2] =
+ ClampToS16((static_cast<s32>(l_sample) + static_cast<s32>(r_sample)) / 2);
+
+ buffer[i * stream_channel_count + 4] = l_sample;
+ buffer[i * stream_channel_count + 5] = r_sample;
+ }
+
+ } else if (channel_count == 6) {
+ const auto fl_sample = ClampToS16(mix_buffers[0][i]);
+ const auto fr_sample = ClampToS16(mix_buffers[1][i]);
+ const auto fc_sample = ClampToS16(mix_buffers[2][i]);
+ const auto lf_sample = ClampToS16(mix_buffers[3][i]);
+ const auto bl_sample = ClampToS16(mix_buffers[4][i]);
+ const auto br_sample = ClampToS16(mix_buffers[5][i]);
+
+ if (stream_channel_count == 1) {
+ buffer[i * stream_channel_count + 0] = fc_sample;
+ } else if (stream_channel_count == 2) {
+ buffer[i * stream_channel_count + 0] =
+ static_cast<s16>(0.3694f * static_cast<float>(fl_sample) +
+ 0.2612f * static_cast<float>(fc_sample) +
+ 0.3694f * static_cast<float>(bl_sample));
+ buffer[i * stream_channel_count + 1] =
+ static_cast<s16>(0.3694f * static_cast<float>(fr_sample) +
+ 0.2612f * static_cast<float>(fc_sample) +
+ 0.3694f * static_cast<float>(br_sample));
+ } else if (stream_channel_count == 6) {
+ buffer[i * stream_channel_count + 0] = fl_sample;
+ buffer[i * stream_channel_count + 1] = fr_sample;
+ buffer[i * stream_channel_count + 2] = fc_sample;
+ buffer[i * stream_channel_count + 3] = lf_sample;
+ buffer[i * stream_channel_count + 4] = bl_sample;
+ buffer[i * stream_channel_count + 5] = br_sample;
+ }
}
}
}
+
audio_out->QueueBuffer(stream, tag, std::move(buffer));
elapsed_frame_count++;
+ voice_context.UpdateStateByDspShared();
}
void AudioRenderer::ReleaseAndQueueBuffers() {
diff --git a/src/audio_core/audio_renderer.h b/src/audio_core/audio_renderer.h
index f0b691a86..2fd93e058 100644
--- a/src/audio_core/audio_renderer.h
+++ b/src/audio_core/audio_renderer.h
@@ -9,12 +9,18 @@
#include <vector>
#include "audio_core/behavior_info.h"
+#include "audio_core/command_generator.h"
#include "audio_core/common.h"
+#include "audio_core/effect_context.h"
+#include "audio_core/memory_pool.h"
+#include "audio_core/mix_context.h"
+#include "audio_core/sink_context.h"
+#include "audio_core/splitter_context.h"
#include "audio_core/stream.h"
+#include "audio_core/voice_context.h"
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/hle/kernel/object.h"
#include "core/hle/result.h"
namespace Core::Timing {
@@ -30,220 +36,25 @@ class Memory;
}
namespace AudioCore {
+using DSPStateHolder = std::array<VoiceState*, 6>;
class AudioOut;
-enum class PlayState : u8 {
- Started = 0,
- Stopped = 1,
- Paused = 2,
-};
-
-enum class Effect : u8 {
- None = 0,
- Aux = 2,
-};
-
-enum class EffectStatus : u8 {
- None = 0,
- New = 1,
-};
-
-struct AudioRendererParameter {
- u32_le sample_rate;
- u32_le sample_count;
- u32_le mix_buffer_count;
- u32_le submix_count;
- u32_le voice_count;
- u32_le sink_count;
- u32_le effect_count;
- u32_le performance_frame_count;
- u8 is_voice_drop_enabled;
- u8 unknown_21;
- u8 unknown_22;
- u8 execution_mode;
- u32_le splitter_count;
- u32_le num_splitter_send_channels;
- u32_le unknown_30;
- u32_le revision;
-};
-static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
-
-enum class MemoryPoolStates : u32 { // Should be LE
- Invalid = 0x0,
- Unknown = 0x1,
- RequestDetach = 0x2,
- Detached = 0x3,
- RequestAttach = 0x4,
- Attached = 0x5,
- Released = 0x6,
-};
-
-struct MemoryPoolEntry {
- MemoryPoolStates state;
- u32_le unknown_4;
- u32_le unknown_8;
- u32_le unknown_c;
-};
-static_assert(sizeof(MemoryPoolEntry) == 0x10, "MemoryPoolEntry has wrong size");
-
-struct MemoryPoolInfo {
- u64_le pool_address;
- u64_le pool_size;
- MemoryPoolStates pool_state;
- INSERT_PADDING_WORDS(3); // Unknown
-};
-static_assert(sizeof(MemoryPoolInfo) == 0x20, "MemoryPoolInfo has wrong size");
-struct BiquadFilter {
- u8 enable;
- INSERT_PADDING_BYTES(1);
- std::array<s16_le, 3> numerator;
- std::array<s16_le, 2> denominator;
-};
-static_assert(sizeof(BiquadFilter) == 0xc, "BiquadFilter has wrong size");
-
-struct WaveBuffer {
- u64_le buffer_addr;
- u64_le buffer_sz;
- s32_le start_sample_offset;
- s32_le end_sample_offset;
- u8 is_looping;
- u8 end_of_stream;
- u8 sent_to_server;
- INSERT_PADDING_BYTES(5);
- u64 context_addr;
- u64 context_sz;
- INSERT_PADDING_BYTES(8);
-};
-static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer has wrong size");
-
-struct VoiceResourceInformation {
- s32_le id{};
- std::array<float_le, MAX_MIX_BUFFERS> mix_volumes{};
- bool in_use{};
- INSERT_PADDING_BYTES(11);
-};
-static_assert(sizeof(VoiceResourceInformation) == 0x70, "VoiceResourceInformation has wrong size");
-
-struct VoiceInfo {
- u32_le id;
- u32_le node_id;
- u8 is_new;
- u8 is_in_use;
- PlayState play_state;
- u8 sample_format;
- u32_le sample_rate;
- u32_le priority;
- u32_le sorting_order;
- u32_le channel_count;
- float_le pitch;
- float_le volume;
- std::array<BiquadFilter, 2> biquad_filter;
- u32_le wave_buffer_count;
- u32_le wave_buffer_head;
- INSERT_PADDING_WORDS(1);
- u64_le additional_params_addr;
- u64_le additional_params_sz;
- u32_le mix_id;
- u32_le splitter_info_id;
- std::array<WaveBuffer, 4> wave_buffer;
- std::array<u32_le, 6> voice_channel_resource_ids;
- INSERT_PADDING_BYTES(24);
-};
-static_assert(sizeof(VoiceInfo) == 0x170, "VoiceInfo is wrong size");
-
-struct VoiceOutStatus {
- u64_le played_sample_count;
- u32_le wave_buffer_consumed;
- u32_le voice_drops_count;
-};
-static_assert(sizeof(VoiceOutStatus) == 0x10, "VoiceOutStatus has wrong size");
-
-struct AuxInfo {
- std::array<u8, 24> input_mix_buffers;
- std::array<u8, 24> output_mix_buffers;
- u32_le mix_buffer_count;
- u32_le sample_rate; // Stored in the aux buffer currently
- u32_le sample_count;
- u64_le send_buffer_info;
- u64_le send_buffer_base;
-
- u64_le return_buffer_info;
- u64_le return_buffer_base;
-};
-static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
-
-struct EffectInStatus {
- Effect type;
- u8 is_new;
- u8 is_enabled;
- INSERT_PADDING_BYTES(1);
- u32_le mix_id;
- u64_le buffer_base;
- u64_le buffer_sz;
- s32_le priority;
- INSERT_PADDING_BYTES(4);
- union {
- std::array<u8, 0xa0> raw;
- AuxInfo aux_info;
- };
-};
-static_assert(sizeof(EffectInStatus) == 0xc0, "EffectInStatus is an invalid size");
-
-struct EffectOutStatus {
- EffectStatus state;
- INSERT_PADDING_BYTES(0xf);
-};
-static_assert(sizeof(EffectOutStatus) == 0x10, "EffectOutStatus is an invalid size");
-
struct RendererInfo {
u64_le elasped_frame_count{};
INSERT_PADDING_WORDS(2);
};
static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
-struct UpdateDataHeader {
- UpdateDataHeader() {}
-
- explicit UpdateDataHeader(const AudioRendererParameter& config) {
- revision = Common::MakeMagic('R', 'E', 'V', '8'); // 9.2.0 Revision
- behavior_size = 0xb0;
- memory_pools_size = (config.effect_count + (config.voice_count * 4)) * 0x10;
- voices_size = config.voice_count * 0x10;
- voice_resource_size = 0x0;
- effects_size = config.effect_count * 0x10;
- mixes_size = 0x0;
- sinks_size = config.sink_count * 0x20;
- performance_manager_size = 0x10;
- render_info = 0;
- total_size = sizeof(UpdateDataHeader) + behavior_size + memory_pools_size + voices_size +
- effects_size + sinks_size + performance_manager_size;
- }
-
- u32_le revision{};
- u32_le behavior_size{};
- u32_le memory_pools_size{};
- u32_le voices_size{};
- u32_le voice_resource_size{};
- u32_le effects_size{};
- u32_le mixes_size{};
- u32_le sinks_size{};
- u32_le performance_manager_size{};
- u32_le splitter_size{};
- u32_le render_info{};
- INSERT_PADDING_WORDS(4);
- u32_le total_size{};
-};
-static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader has wrong size");
-
class AudioRenderer {
public:
AudioRenderer(Core::Timing::CoreTiming& core_timing, Core::Memory::Memory& memory_,
- AudioRendererParameter params,
+ AudioCommon::AudioRendererParameter params,
std::shared_ptr<Kernel::WritableEvent> buffer_event, std::size_t instance_number);
~AudioRenderer();
- ResultVal<std::vector<u8>> UpdateAudioRenderer(const std::vector<u8>& input_params);
+ ResultCode UpdateAudioRenderer(const std::vector<u8>& input_params,
+ std::vector<u8>& output_params);
void QueueMixedBuffer(Buffer::Tag tag);
void ReleaseAndQueueBuffers();
u32 GetSampleRate() const;
@@ -252,19 +63,23 @@ public:
Stream::State GetStreamState() const;
private:
- class EffectState;
- class VoiceState;
BehaviorInfo behavior_info{};
- AudioRendererParameter worker_params;
+ AudioCommon::AudioRendererParameter worker_params;
std::shared_ptr<Kernel::WritableEvent> buffer_event;
+ std::vector<ServerMemoryPoolInfo> memory_pool_info;
+ VoiceContext voice_context;
+ EffectContext effect_context;
+ MixContext mix_context;
+ SinkContext sink_context;
+ SplitterContext splitter_context;
std::vector<VoiceState> voices;
- std::vector<VoiceResourceInformation> voice_resources;
- std::vector<EffectState> effects;
std::unique_ptr<AudioOut> audio_out;
StreamPtr stream;
Core::Memory::Memory& memory;
+ CommandGenerator command_generator;
std::size_t elapsed_frame_count{};
+ std::vector<s32> temp_mix_buffer{};
};
} // namespace AudioCore
diff --git a/src/audio_core/behavior_info.cpp b/src/audio_core/behavior_info.cpp
index 94b7a3bf1..3c2e3e6f1 100644
--- a/src/audio_core/behavior_info.cpp
+++ b/src/audio_core/behavior_info.cpp
@@ -9,39 +9,11 @@
namespace AudioCore {
-BehaviorInfo::BehaviorInfo() : process_revision(CURRENT_PROCESS_REVISION) {}
+BehaviorInfo::BehaviorInfo() : process_revision(AudioCommon::CURRENT_PROCESS_REVISION) {}
BehaviorInfo::~BehaviorInfo() = default;
-bool BehaviorInfo::UpdateInput(const std::vector<u8>& buffer, std::size_t offset) {
- if (!CanConsumeBuffer(buffer.size(), offset, sizeof(InParams))) {
- LOG_ERROR(Audio, "Buffer is an invalid size!");
- return false;
- }
- InParams params{};
- std::memcpy(&params, buffer.data() + offset, sizeof(InParams));
-
- if (!IsValidRevision(params.revision)) {
- LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", params.revision);
- return false;
- }
-
- if (user_revision != params.revision) {
- LOG_ERROR(Audio,
- "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}",
- user_revision, params.revision);
- return false;
- }
-
- ClearError();
- UpdateFlags(params.flags);
-
- // TODO(ogniK): Check input params size when InfoUpdater is used
-
- return true;
-}
-
bool BehaviorInfo::UpdateOutput(std::vector<u8>& buffer, std::size_t offset) {
- if (!CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) {
+ if (!AudioCommon::CanConsumeBuffer(buffer.size(), offset, sizeof(OutParams))) {
LOG_ERROR(Audio, "Buffer is an invalid size!");
return false;
}
@@ -65,36 +37,69 @@ void BehaviorInfo::SetUserRevision(u32_le revision) {
user_revision = revision;
}
+u32_le BehaviorInfo::GetUserRevision() const {
+ return user_revision;
+}
+
+u32_le BehaviorInfo::GetProcessRevision() const {
+ return process_revision;
+}
+
bool BehaviorInfo::IsAdpcmLoopContextBugFixed() const {
- return IsRevisionSupported(2, user_revision);
+ return AudioCommon::IsRevisionSupported(2, user_revision);
}
bool BehaviorInfo::IsSplitterSupported() const {
- return IsRevisionSupported(2, user_revision);
+ return AudioCommon::IsRevisionSupported(2, user_revision);
}
bool BehaviorInfo::IsLongSizePreDelaySupported() const {
- return IsRevisionSupported(3, user_revision);
+ return AudioCommon::IsRevisionSupported(3, user_revision);
}
-bool BehaviorInfo::IsAudioRenererProcessingTimeLimit80PercentSupported() const {
- return IsRevisionSupported(5, user_revision);
+bool BehaviorInfo::IsAudioRendererProcessingTimeLimit80PercentSupported() const {
+ return AudioCommon::IsRevisionSupported(5, user_revision);
}
-bool BehaviorInfo::IsAudioRenererProcessingTimeLimit75PercentSupported() const {
- return IsRevisionSupported(4, user_revision);
+bool BehaviorInfo::IsAudioRendererProcessingTimeLimit75PercentSupported() const {
+ return AudioCommon::IsRevisionSupported(4, user_revision);
}
-bool BehaviorInfo::IsAudioRenererProcessingTimeLimit70PercentSupported() const {
- return IsRevisionSupported(1, user_revision);
+bool BehaviorInfo::IsAudioRendererProcessingTimeLimit70PercentSupported() const {
+ return AudioCommon::IsRevisionSupported(1, user_revision);
}
bool BehaviorInfo::IsElapsedFrameCountSupported() const {
- return IsRevisionSupported(5, user_revision);
+ return AudioCommon::IsRevisionSupported(5, user_revision);
}
bool BehaviorInfo::IsMemoryPoolForceMappingEnabled() const {
return (flags & 1) != 0;
}
+bool BehaviorInfo::IsFlushVoiceWaveBuffersSupported() const {
+ return AudioCommon::IsRevisionSupported(5, user_revision);
+}
+
+bool BehaviorInfo::IsVoicePlayedSampleCountResetAtLoopPointSupported() const {
+ return AudioCommon::IsRevisionSupported(5, user_revision);
+}
+
+bool BehaviorInfo::IsVoicePitchAndSrcSkippedSupported() const {
+ return AudioCommon::IsRevisionSupported(5, user_revision);
+}
+
+bool BehaviorInfo::IsMixInParameterDirtyOnlyUpdateSupported() const {
+ return AudioCommon::IsRevisionSupported(7, user_revision);
+}
+
+bool BehaviorInfo::IsSplitterBugFixed() const {
+ return AudioCommon::IsRevisionSupported(5, user_revision);
+}
+
+void BehaviorInfo::CopyErrorInfo(BehaviorInfo::OutParams& dst) {
+ dst.error_count = static_cast<u32>(error_count);
+ std::copy(errors.begin(), errors.begin() + error_count, dst.errors.begin());
+}
+
} // namespace AudioCore
diff --git a/src/audio_core/behavior_info.h b/src/audio_core/behavior_info.h
index c5e91ab39..512a4ebe3 100644
--- a/src/audio_core/behavior_info.h
+++ b/src/audio_core/behavior_info.h
@@ -14,53 +14,59 @@
namespace AudioCore {
class BehaviorInfo {
public:
+ struct ErrorInfo {
+ u32_le result{};
+ INSERT_PADDING_WORDS(1);
+ u64_le result_info{};
+ };
+ static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
+
+ struct InParams {
+ u32_le revision{};
+ u32_le padding{};
+ u64_le flags{};
+ };
+ static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
+
+ struct OutParams {
+ std::array<ErrorInfo, 10> errors{};
+ u32_le error_count{};
+ INSERT_PADDING_BYTES(12);
+ };
+ static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
+
explicit BehaviorInfo();
~BehaviorInfo();
- bool UpdateInput(const std::vector<u8>& buffer, std::size_t offset);
bool UpdateOutput(std::vector<u8>& buffer, std::size_t offset);
void ClearError();
void UpdateFlags(u64_le dest_flags);
void SetUserRevision(u32_le revision);
+ u32_le GetUserRevision() const;
+ u32_le GetProcessRevision() const;
bool IsAdpcmLoopContextBugFixed() const;
bool IsSplitterSupported() const;
bool IsLongSizePreDelaySupported() const;
- bool IsAudioRenererProcessingTimeLimit80PercentSupported() const;
- bool IsAudioRenererProcessingTimeLimit75PercentSupported() const;
- bool IsAudioRenererProcessingTimeLimit70PercentSupported() const;
+ bool IsAudioRendererProcessingTimeLimit80PercentSupported() const;
+ bool IsAudioRendererProcessingTimeLimit75PercentSupported() const;
+ bool IsAudioRendererProcessingTimeLimit70PercentSupported() const;
bool IsElapsedFrameCountSupported() const;
bool IsMemoryPoolForceMappingEnabled() const;
+ bool IsFlushVoiceWaveBuffersSupported() const;
+ bool IsVoicePlayedSampleCountResetAtLoopPointSupported() const;
+ bool IsVoicePitchAndSrcSkippedSupported() const;
+ bool IsMixInParameterDirtyOnlyUpdateSupported() const;
+ bool IsSplitterBugFixed() const;
+ void CopyErrorInfo(OutParams& dst);
private:
u32_le process_revision{};
u32_le user_revision{};
u64_le flags{};
-
- struct ErrorInfo {
- u32_le result{};
- INSERT_PADDING_WORDS(1);
- u64_le result_info{};
- };
- static_assert(sizeof(ErrorInfo) == 0x10, "ErrorInfo is an invalid size");
-
std::array<ErrorInfo, 10> errors{};
std::size_t error_count{};
-
- struct InParams {
- u32_le revision{};
- u32_le padding{};
- u64_le flags{};
- };
- static_assert(sizeof(InParams) == 0x10, "InParams is an invalid size");
-
- struct OutParams {
- std::array<ErrorInfo, 10> errors{};
- u32_le error_count{};
- INSERT_PADDING_BYTES(12);
- };
- static_assert(sizeof(OutParams) == 0xb0, "OutParams is an invalid size");
};
} // namespace AudioCore
diff --git a/src/audio_core/codec.cpp b/src/audio_core/codec.cpp
index c5a0d98ce..2fb91c13a 100644
--- a/src/audio_core/codec.cpp
+++ b/src/audio_core/codec.cpp
@@ -16,8 +16,9 @@ std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM
constexpr std::size_t FRAME_LEN = 8;
constexpr std::size_t SAMPLES_PER_FRAME = 14;
- constexpr std::array<int, 16> SIGNED_NIBBLES = {
- {0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1}};
+ static constexpr std::array<int, 16> SIGNED_NIBBLES{
+ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1,
+ };
const std::size_t sample_count = (size / FRAME_LEN) * SAMPLES_PER_FRAME;
const std::size_t ret_size =
diff --git a/src/audio_core/codec.h b/src/audio_core/codec.h
index ef2ce01a8..9507abb1b 100644
--- a/src/audio_core/codec.h
+++ b/src/audio_core/codec.h
@@ -38,7 +38,7 @@ using ADPCM_Coeff = std::array<s16, 16>;
* @param state ADPCM state, this is updated with new state
* @return Decoded stereo signed PCM16 data, sample_count in length
*/
-std::vector<s16> DecodeADPCM(const u8* const data, std::size_t size, const ADPCM_Coeff& coeff,
+std::vector<s16> DecodeADPCM(const u8* data, std::size_t size, const ADPCM_Coeff& coeff,
ADPCMState& state);
}; // namespace AudioCore::Codec
diff --git a/src/audio_core/command_generator.cpp b/src/audio_core/command_generator.cpp
new file mode 100644
index 000000000..fb8700ccf
--- /dev/null
+++ b/src/audio_core/command_generator.cpp
@@ -0,0 +1,977 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/algorithm/interpolate.h"
+#include "audio_core/command_generator.h"
+#include "audio_core/effect_context.h"
+#include "audio_core/mix_context.h"
+#include "audio_core/voice_context.h"
+#include "core/memory.h"
+
+namespace AudioCore {
+namespace {
+constexpr std::size_t MIX_BUFFER_SIZE = 0x3f00;
+constexpr std::size_t SCALED_MIX_BUFFER_SIZE = MIX_BUFFER_SIZE << 15ULL;
+
+template <std::size_t N>
+void ApplyMix(s32* output, const s32* input, s32 gain, s32 sample_count) {
+ for (std::size_t i = 0; i < static_cast<std::size_t>(sample_count); i += N) {
+ for (std::size_t j = 0; j < N; j++) {
+ output[i + j] +=
+ static_cast<s32>((static_cast<s64>(input[i + j]) * gain + 0x4000) >> 15);
+ }
+ }
+}
+
+s32 ApplyMixRamp(s32* output, const s32* input, float gain, float delta, s32 sample_count) {
+ s32 x = 0;
+ for (s32 i = 0; i < sample_count; i++) {
+ x = static_cast<s32>(static_cast<float>(input[i]) * gain);
+ output[i] += x;
+ gain += delta;
+ }
+ return x;
+}
+
+void ApplyGain(s32* output, const s32* input, s32 gain, s32 delta, s32 sample_count) {
+ for (s32 i = 0; i < sample_count; i++) {
+ output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
+ gain += delta;
+ }
+}
+
+void ApplyGainWithoutDelta(s32* output, const s32* input, s32 gain, s32 sample_count) {
+ for (s32 i = 0; i < sample_count; i++) {
+ output[i] = static_cast<s32>((static_cast<s64>(input[i]) * gain + 0x4000) >> 15);
+ }
+}
+
+s32 ApplyMixDepop(s32* output, s32 first_sample, s32 delta, s32 sample_count) {
+ const bool positive = first_sample > 0;
+ auto final_sample = std::abs(first_sample);
+ for (s32 i = 0; i < sample_count; i++) {
+ final_sample = static_cast<s32>((static_cast<s64>(final_sample) * delta) >> 15);
+ if (positive) {
+ output[i] += final_sample;
+ } else {
+ output[i] -= final_sample;
+ }
+ }
+ if (positive) {
+ return final_sample;
+ } else {
+ return -final_sample;
+ }
+}
+
+} // namespace
+
+CommandGenerator::CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
+ VoiceContext& voice_context, MixContext& mix_context,
+ SplitterContext& splitter_context, EffectContext& effect_context,
+ Core::Memory::Memory& memory)
+ : worker_params(worker_params), voice_context(voice_context), mix_context(mix_context),
+ splitter_context(splitter_context), effect_context(effect_context), memory(memory),
+ mix_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
+ worker_params.sample_count),
+ sample_buffer(MIX_BUFFER_SIZE),
+ depop_buffer((worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT) *
+ worker_params.sample_count) {}
+CommandGenerator::~CommandGenerator() = default;
+
+void CommandGenerator::ClearMixBuffers() {
+ std::fill(mix_buffer.begin(), mix_buffer.end(), 0);
+ std::fill(sample_buffer.begin(), sample_buffer.end(), 0);
+ // std::fill(depop_buffer.begin(), depop_buffer.end(), 0);
+}
+
+void CommandGenerator::GenerateVoiceCommands() {
+ if (dumping_frame) {
+ LOG_DEBUG(Audio, "(DSP_TRACE) GenerateVoiceCommands");
+ }
+ // Grab all our voices
+ const auto voice_count = voice_context.GetVoiceCount();
+ for (std::size_t i = 0; i < voice_count; i++) {
+ auto& voice_info = voice_context.GetSortedInfo(i);
+ // Update voices and check if we should queue them
+ if (voice_info.ShouldSkip() || !voice_info.UpdateForCommandGeneration(voice_context)) {
+ continue;
+ }
+
+ // Queue our voice
+ GenerateVoiceCommand(voice_info);
+ }
+ // Update our splitters
+ splitter_context.UpdateInternalState();
+}
+
+void CommandGenerator::GenerateVoiceCommand(ServerVoiceInfo& voice_info) {
+ auto& in_params = voice_info.GetInParams();
+ const auto channel_count = in_params.channel_count;
+
+ for (s32 channel = 0; channel < channel_count; channel++) {
+ const auto resource_id = in_params.voice_channel_resource_id[channel];
+ auto& dsp_state = voice_context.GetDspSharedState(resource_id);
+ auto& channel_resource = voice_context.GetChannelResource(resource_id);
+
+ // Decode our samples for our channel
+ GenerateDataSourceCommand(voice_info, dsp_state, channel);
+
+ if (in_params.should_depop) {
+ in_params.last_volume = 0.0f;
+ } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER ||
+ in_params.mix_id != AudioCommon::NO_MIX) {
+ // Apply a biquad filter if needed
+ GenerateBiquadFilterCommandForVoice(voice_info, dsp_state,
+ worker_params.mix_buffer_count, channel);
+ // Base voice volume ramping
+ GenerateVolumeRampCommand(in_params.last_volume, in_params.volume, channel,
+ in_params.node_id);
+ in_params.last_volume = in_params.volume;
+
+ if (in_params.mix_id != AudioCommon::NO_MIX) {
+ // If we're using a mix id
+ auto& mix_info = mix_context.GetInfo(in_params.mix_id);
+ const auto& dest_mix_params = mix_info.GetInParams();
+
+ // Voice Mixing
+ GenerateVoiceMixCommand(
+ channel_resource.GetCurrentMixVolume(), channel_resource.GetLastMixVolume(),
+ dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
+ worker_params.mix_buffer_count + channel, in_params.node_id);
+
+ // Update last mix volumes
+ channel_resource.UpdateLastMixVolumes();
+ } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
+ s32 base = channel;
+ while (auto* destination_data =
+ GetDestinationData(in_params.splitter_info_id, base)) {
+ base += channel_count;
+
+ if (!destination_data->IsConfigured()) {
+ continue;
+ }
+ if (destination_data->GetMixId() >= static_cast<int>(mix_context.GetCount())) {
+ continue;
+ }
+
+ const auto& mix_info = mix_context.GetInfo(destination_data->GetMixId());
+ const auto& dest_mix_params = mix_info.GetInParams();
+ GenerateVoiceMixCommand(
+ destination_data->CurrentMixVolumes(), destination_data->LastMixVolumes(),
+ dsp_state, dest_mix_params.buffer_offset, dest_mix_params.buffer_count,
+ worker_params.mix_buffer_count + channel, in_params.node_id);
+ destination_data->MarkDirty();
+ }
+ }
+ // Update biquad filter enabled states
+ for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
+ in_params.was_biquad_filter_enabled[i] = in_params.biquad_filter[i].enabled;
+ }
+ }
+ }
+}
+
+void CommandGenerator::GenerateSubMixCommands() {
+ const auto mix_count = mix_context.GetCount();
+ for (std::size_t i = 0; i < mix_count; i++) {
+ auto& mix_info = mix_context.GetSortedInfo(i);
+ const auto& in_params = mix_info.GetInParams();
+ if (!in_params.in_use || in_params.mix_id == AudioCommon::FINAL_MIX) {
+ continue;
+ }
+ GenerateSubMixCommand(mix_info);
+ }
+}
+
+void CommandGenerator::GenerateFinalMixCommands() {
+ GenerateFinalMixCommand();
+}
+
+void CommandGenerator::PreCommand() {
+ if (!dumping_frame) {
+ return;
+ }
+ for (std::size_t i = 0; i < splitter_context.GetInfoCount(); i++) {
+ const auto& base = splitter_context.GetInfo(i);
+ std::string graph = fmt::format("b[{}]", i);
+ const auto* head = base.GetHead();
+ while (head != nullptr) {
+ graph += fmt::format("->{}", head->GetMixId());
+ head = head->GetNextDestination();
+ }
+ LOG_DEBUG(Audio, "(DSP_TRACE) SplitterGraph splitter_info={}, {}", i, graph);
+ }
+}
+
+void CommandGenerator::PostCommand() {
+ if (!dumping_frame) {
+ return;
+ }
+ dumping_frame = false;
+}
+
+void CommandGenerator::GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
+ s32 channel) {
+ const auto& in_params = voice_info.GetInParams();
+ const auto depop = in_params.should_depop;
+
+ if (depop) {
+ if (in_params.mix_id != AudioCommon::NO_MIX) {
+ auto& mix_info = mix_context.GetInfo(in_params.mix_id);
+ const auto& mix_in = mix_info.GetInParams();
+ GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
+ } else if (in_params.splitter_info_id != AudioCommon::NO_SPLITTER) {
+ s32 index{};
+ while (const auto* destination =
+ GetDestinationData(in_params.splitter_info_id, index++)) {
+ if (!destination->IsConfigured()) {
+ continue;
+ }
+ auto& mix_info = mix_context.GetInfo(destination->GetMixId());
+ const auto& mix_in = mix_info.GetInParams();
+ GenerateDepopPrepareCommand(dsp_state, mix_in.buffer_count, mix_in.buffer_offset);
+ }
+ }
+ } else {
+ switch (in_params.sample_format) {
+ case SampleFormat::Pcm16:
+ DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(channel), dsp_state, channel,
+ worker_params.sample_rate, worker_params.sample_count,
+ in_params.node_id);
+ break;
+ case SampleFormat::Adpcm:
+ ASSERT(channel == 0 && in_params.channel_count == 1);
+ DecodeFromWaveBuffers(voice_info, GetChannelMixBuffer(0), dsp_state, 0,
+ worker_params.sample_rate, worker_params.sample_count,
+ in_params.node_id);
+ break;
+ default:
+ UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
+ }
+ }
+}
+
+void CommandGenerator::GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info,
+ VoiceState& dsp_state,
+ s32 mix_buffer_count, s32 channel) {
+ for (std::size_t i = 0; i < AudioCommon::MAX_BIQUAD_FILTERS; i++) {
+ const auto& in_params = voice_info.GetInParams();
+ auto& biquad_filter = in_params.biquad_filter[i];
+ // Check if biquad filter is actually used
+ if (!biquad_filter.enabled) {
+ continue;
+ }
+
+ // Reinitialize our biquad filter state if it was enabled previously
+ if (!in_params.was_biquad_filter_enabled[i]) {
+ dsp_state.biquad_filter_state.fill(0);
+ }
+
+ // Generate biquad filter
+ // GenerateBiquadFilterCommand(mix_buffer_count, biquad_filter,
+ // dsp_state.biquad_filter_state,
+ // mix_buffer_count + channel, mix_buffer_count +
+ // channel, worker_params.sample_count,
+ // voice_info.GetInParams().node_id);
+ }
+}
+
+void AudioCore::CommandGenerator::GenerateBiquadFilterCommand(
+ s32 mix_buffer, const BiquadFilterParameter& params, std::array<s64, 2>& state,
+ std::size_t input_offset, std::size_t output_offset, s32 sample_count, s32 node_id) {
+ if (dumping_frame) {
+ LOG_DEBUG(Audio,
+ "(DSP_TRACE) GenerateBiquadFilterCommand node_id={}, "
+ "input_mix_buffer={}, output_mix_buffer={}",
+ node_id, input_offset, output_offset);
+ }
+ const auto* input = GetMixBuffer(input_offset);
+ auto* output = GetMixBuffer(output_offset);
+
+ // Biquad filter parameters
+ const auto [n0, n1, n2] = params.numerator;
+ const auto [d0, d1] = params.denominator;
+
+ // Biquad filter states
+ auto [s0, s1] = state;
+
+ constexpr s64 int32_min = std::numeric_limits<s32>::min();
+ constexpr s64 int32_max = std::numeric_limits<s32>::max();
+
+ for (int i = 0; i < sample_count; ++i) {
+ const auto sample = static_cast<s64>(input[i]);
+ const auto f = (sample * n0 + s0 + 0x4000) >> 15;
+ const auto y = std::clamp(f, int32_min, int32_max);
+ s0 = sample * n1 + y * d0 + s1;
+ s1 = sample * n2 + y * d1;
+ output[i] = static_cast<s32>(y);
+ }
+
+ state = {s0, s1};
+}
+
+void CommandGenerator::GenerateDepopPrepareCommand(VoiceState& dsp_state,
+ std::size_t mix_buffer_count,
+ std::size_t mix_buffer_offset) {
+ for (std::size_t i = 0; i < mix_buffer_count; i++) {
+ auto& sample = dsp_state.previous_samples[i];
+ if (sample != 0) {
+ depop_buffer[mix_buffer_offset + i] += sample;
+ sample = 0;
+ }
+ }
+}
+
+void CommandGenerator::GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
+ std::size_t mix_buffer_offset,
+ s32 sample_rate) {
+ const std::size_t end_offset =
+ std::min(mix_buffer_offset + mix_buffer_count, GetTotalMixBufferCount());
+ const s32 delta = sample_rate == 48000 ? 0x7B29 : 0x78CB;
+ for (std::size_t i = mix_buffer_offset; i < end_offset; i++) {
+ if (depop_buffer[i] == 0) {
+ continue;
+ }
+
+ depop_buffer[i] =
+ ApplyMixDepop(GetMixBuffer(i), depop_buffer[i], delta, worker_params.sample_count);
+ }
+}
+
+void CommandGenerator::GenerateEffectCommand(ServerMixInfo& mix_info) {
+ const std::size_t effect_count = effect_context.GetCount();
+ const auto buffer_offset = mix_info.GetInParams().buffer_offset;
+ for (std::size_t i = 0; i < effect_count; i++) {
+ const auto index = mix_info.GetEffectOrder(i);
+ if (index == AudioCommon::NO_EFFECT_ORDER) {
+ break;
+ }
+ auto* info = effect_context.GetInfo(index);
+ const auto type = info->GetType();
+
+ // TODO(ogniK): Finish remaining effects
+ switch (type) {
+ case EffectType::Aux:
+ GenerateAuxCommand(buffer_offset, info, info->IsEnabled());
+ break;
+ case EffectType::I3dl2Reverb:
+ GenerateI3dl2ReverbEffectCommand(buffer_offset, info, info->IsEnabled());
+ break;
+ case EffectType::BiquadFilter:
+ GenerateBiquadFilterEffectCommand(buffer_offset, info, info->IsEnabled());
+ break;
+ default:
+ break;
+ }
+
+ info->UpdateForCommandGeneration();
+ }
+}
+
+void CommandGenerator::GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info,
+ bool enabled) {
+ if (!enabled) {
+ return;
+ }
+ const auto& params = dynamic_cast<EffectI3dl2Reverb*>(info)->GetParams();
+ const auto channel_count = params.channel_count;
+ for (s32 i = 0; i < channel_count; i++) {
+ // TODO(ogniK): Actually implement reverb
+ if (params.input[i] != params.output[i]) {
+ const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
+ auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
+ ApplyMix<1>(output, input, 32768, worker_params.sample_count);
+ }
+ }
+}
+
+void CommandGenerator::GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info,
+ bool enabled) {
+ if (!enabled) {
+ return;
+ }
+ const auto& params = dynamic_cast<EffectBiquadFilter*>(info)->GetParams();
+ const auto channel_count = params.channel_count;
+ for (s32 i = 0; i < channel_count; i++) {
+ // TODO(ogniK): Actually implement biquad filter
+ if (params.input[i] != params.output[i]) {
+ const auto* input = GetMixBuffer(mix_buffer_offset + params.input[i]);
+ auto* output = GetMixBuffer(mix_buffer_offset + params.output[i]);
+ ApplyMix<1>(output, input, 32768, worker_params.sample_count);
+ }
+ }
+}
+
+void CommandGenerator::GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled) {
+ auto* aux = dynamic_cast<EffectAuxInfo*>(info);
+ const auto& params = aux->GetParams();
+ if (aux->GetSendBuffer() != 0 && aux->GetRecvBuffer() != 0) {
+ const auto max_channels = params.count;
+ u32 offset{};
+ for (u32 channel = 0; channel < max_channels; channel++) {
+ u32 write_count = 0;
+ if (channel == (max_channels - 1)) {
+ write_count = offset + worker_params.sample_count;
+ }
+
+ const auto input_index = params.input_mix_buffers[channel] + mix_buffer_offset;
+ const auto output_index = params.output_mix_buffers[channel] + mix_buffer_offset;
+
+ if (enabled) {
+ AuxInfoDSP send_info{};
+ AuxInfoDSP recv_info{};
+ memory.ReadBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
+ memory.ReadBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
+
+ WriteAuxBuffer(send_info, aux->GetSendBuffer(), params.sample_count,
+ GetMixBuffer(input_index), worker_params.sample_count, offset,
+ write_count);
+ memory.WriteBlock(aux->GetSendInfo(), &send_info, sizeof(AuxInfoDSP));
+
+ const auto samples_read = ReadAuxBuffer(
+ recv_info, aux->GetRecvBuffer(), params.sample_count,
+ GetMixBuffer(output_index), worker_params.sample_count, offset, write_count);
+ memory.WriteBlock(aux->GetRecvInfo(), &recv_info, sizeof(AuxInfoDSP));
+
+ if (samples_read != static_cast<int>(worker_params.sample_count) &&
+ samples_read <= params.sample_count) {
+ std::memset(GetMixBuffer(output_index), 0, params.sample_count - samples_read);
+ }
+ } else {
+ AuxInfoDSP empty{};
+ memory.WriteBlock(aux->GetSendInfo(), &empty, sizeof(AuxInfoDSP));
+ memory.WriteBlock(aux->GetRecvInfo(), &empty, sizeof(AuxInfoDSP));
+ if (output_index != input_index) {
+ std::memcpy(GetMixBuffer(output_index), GetMixBuffer(input_index),
+ worker_params.sample_count * sizeof(s32));
+ }
+ }
+
+ offset += worker_params.sample_count;
+ }
+ }
+}
+
+ServerSplitterDestinationData* CommandGenerator::GetDestinationData(s32 splitter_id, s32 index) {
+ if (splitter_id == AudioCommon::NO_SPLITTER) {
+ return nullptr;
+ }
+ return splitter_context.GetDestinationData(splitter_id, index);
+}
+
+s32 CommandGenerator::WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples,
+ const s32* data, u32 sample_count, u32 write_offset,
+ u32 write_count) {
+ if (max_samples == 0) {
+ return 0;
+ }
+ u32 offset = dsp_info.write_offset + write_offset;
+ if (send_buffer == 0 || offset > max_samples) {
+ return 0;
+ }
+
+ std::size_t data_offset{};
+ u32 remaining = sample_count;
+ while (remaining > 0) {
+ // Get position in buffer
+ const auto base = send_buffer + (offset * sizeof(u32));
+ const auto samples_to_grab = std::min(max_samples - offset, remaining);
+ // Write to output
+ memory.WriteBlock(base, (data + data_offset), samples_to_grab * sizeof(u32));
+ offset = (offset + samples_to_grab) % max_samples;
+ remaining -= samples_to_grab;
+ data_offset += samples_to_grab;
+ }
+
+ if (write_count != 0) {
+ dsp_info.write_offset = (dsp_info.write_offset + write_count) % max_samples;
+ }
+ return sample_count;
+}
+
+s32 CommandGenerator::ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples,
+ s32* out_data, u32 sample_count, u32 read_offset,
+ u32 read_count) {
+ if (max_samples == 0) {
+ return 0;
+ }
+
+ u32 offset = recv_info.read_offset + read_offset;
+ if (recv_buffer == 0 || offset > max_samples) {
+ return 0;
+ }
+
+ u32 remaining = sample_count;
+ while (remaining > 0) {
+ const auto base = recv_buffer + (offset * sizeof(u32));
+ const auto samples_to_grab = std::min(max_samples - offset, remaining);
+ std::vector<s32> buffer(samples_to_grab);
+ memory.ReadBlock(base, buffer.data(), buffer.size() * sizeof(u32));
+ std::memcpy(out_data, buffer.data(), buffer.size() * sizeof(u32));
+ out_data += samples_to_grab;
+ offset = (offset + samples_to_grab) % max_samples;
+ remaining -= samples_to_grab;
+ }
+
+ if (read_count != 0) {
+ recv_info.read_offset = (recv_info.read_offset + read_count) % max_samples;
+ }
+ return sample_count;
+}
+
+void CommandGenerator::GenerateVolumeRampCommand(float last_volume, float current_volume,
+ s32 channel, s32 node_id) {
+ const auto last = static_cast<s32>(last_volume * 32768.0f);
+ const auto current = static_cast<s32>(current_volume * 32768.0f);
+ const auto delta = static_cast<s32>((static_cast<float>(current) - static_cast<float>(last)) /
+ static_cast<float>(worker_params.sample_count));
+
+ if (dumping_frame) {
+ LOG_DEBUG(Audio,
+ "(DSP_TRACE) GenerateVolumeRampCommand node_id={}, input={}, output={}, "
+ "last_volume={}, current_volume={}",
+ node_id, GetMixChannelBufferOffset(channel), GetMixChannelBufferOffset(channel),
+ last_volume, current_volume);
+ }
+ // Apply generic gain on samples
+ ApplyGain(GetChannelMixBuffer(channel), GetChannelMixBuffer(channel), last, delta,
+ worker_params.sample_count);
+}
+
+void CommandGenerator::GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
+ const MixVolumeBuffer& last_mix_volumes,
+ VoiceState& dsp_state, s32 mix_buffer_offset,
+ s32 mix_buffer_count, s32 voice_index, s32 node_id) {
+ // Loop all our mix buffers
+ for (s32 i = 0; i < mix_buffer_count; i++) {
+ if (last_mix_volumes[i] != 0.0f || mix_volumes[i] != 0.0f) {
+ const auto delta = static_cast<float>((mix_volumes[i] - last_mix_volumes[i])) /
+ static_cast<float>(worker_params.sample_count);
+
+ if (dumping_frame) {
+ LOG_DEBUG(Audio,
+ "(DSP_TRACE) GenerateVoiceMixCommand node_id={}, input={}, "
+ "output={}, last_volume={}, current_volume={}",
+ node_id, voice_index, mix_buffer_offset + i, last_mix_volumes[i],
+ mix_volumes[i]);
+ }
+
+ dsp_state.previous_samples[i] =
+ ApplyMixRamp(GetMixBuffer(mix_buffer_offset + i), GetMixBuffer(voice_index),
+ last_mix_volumes[i], delta, worker_params.sample_count);
+ } else {
+ dsp_state.previous_samples[i] = 0;
+ }
+ }
+}
+
+void CommandGenerator::GenerateSubMixCommand(ServerMixInfo& mix_info) {
+ if (dumping_frame) {
+ LOG_DEBUG(Audio, "(DSP_TRACE) GenerateSubMixCommand");
+ }
+ const auto& in_params = mix_info.GetInParams();
+ GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
+ in_params.sample_rate);
+
+ GenerateEffectCommand(mix_info);
+
+ GenerateMixCommands(mix_info);
+}
+
+void CommandGenerator::GenerateMixCommands(ServerMixInfo& mix_info) {
+ if (!mix_info.HasAnyConnection()) {
+ return;
+ }
+ const auto& in_params = mix_info.GetInParams();
+ if (in_params.dest_mix_id != AudioCommon::NO_MIX) {
+ const auto& dest_mix = mix_context.GetInfo(in_params.dest_mix_id);
+ const auto& dest_in_params = dest_mix.GetInParams();
+
+ const auto buffer_count = in_params.buffer_count;
+
+ for (s32 i = 0; i < buffer_count; i++) {
+ for (s32 j = 0; j < dest_in_params.buffer_count; j++) {
+ const auto mixed_volume = in_params.volume * in_params.mix_volume[i][j];
+ if (mixed_volume != 0.0f) {
+ GenerateMixCommand(dest_in_params.buffer_offset + j,
+ in_params.buffer_offset + i, mixed_volume,
+ in_params.node_id);
+ }
+ }
+ }
+ } else if (in_params.splitter_id != AudioCommon::NO_SPLITTER) {
+ s32 base{};
+ while (const auto* destination_data = GetDestinationData(in_params.splitter_id, base++)) {
+ if (!destination_data->IsConfigured()) {
+ continue;
+ }
+
+ const auto& dest_mix = mix_context.GetInfo(destination_data->GetMixId());
+ const auto& dest_in_params = dest_mix.GetInParams();
+ const auto mix_index = (base - 1) % in_params.buffer_count + in_params.buffer_offset;
+ for (std::size_t i = 0; i < static_cast<std::size_t>(dest_in_params.buffer_count);
+ i++) {
+ const auto mixed_volume = in_params.volume * destination_data->GetMixVolume(i);
+ if (mixed_volume != 0.0f) {
+ GenerateMixCommand(dest_in_params.buffer_offset + i, mix_index, mixed_volume,
+ in_params.node_id);
+ }
+ }
+ }
+ }
+}
+
+void CommandGenerator::GenerateMixCommand(std::size_t output_offset, std::size_t input_offset,
+ float volume, s32 node_id) {
+
+ if (dumping_frame) {
+ LOG_DEBUG(Audio,
+ "(DSP_TRACE) GenerateMixCommand node_id={}, input={}, output={}, volume={}",
+ node_id, input_offset, output_offset, volume);
+ }
+
+ auto* output = GetMixBuffer(output_offset);
+ const auto* input = GetMixBuffer(input_offset);
+
+ const s32 gain = static_cast<s32>(volume * 32768.0f);
+ // Mix with loop unrolling
+ if (worker_params.sample_count % 4 == 0) {
+ ApplyMix<4>(output, input, gain, worker_params.sample_count);
+ } else if (worker_params.sample_count % 2 == 0) {
+ ApplyMix<2>(output, input, gain, worker_params.sample_count);
+ } else {
+ ApplyMix<1>(output, input, gain, worker_params.sample_count);
+ }
+}
+
+void CommandGenerator::GenerateFinalMixCommand() {
+ if (dumping_frame) {
+ LOG_DEBUG(Audio, "(DSP_TRACE) GenerateFinalMixCommand");
+ }
+ auto& mix_info = mix_context.GetFinalMixInfo();
+ const auto& in_params = mix_info.GetInParams();
+
+ GenerateDepopForMixBuffersCommand(in_params.buffer_count, in_params.buffer_offset,
+ in_params.sample_rate);
+
+ GenerateEffectCommand(mix_info);
+
+ for (s32 i = 0; i < in_params.buffer_count; i++) {
+ const s32 gain = static_cast<s32>(in_params.volume * 32768.0f);
+ if (dumping_frame) {
+ LOG_DEBUG(
+ Audio,
+ "(DSP_TRACE) ApplyGainWithoutDelta node_id={}, input={}, output={}, volume={}",
+ in_params.node_id, in_params.buffer_offset + i, in_params.buffer_offset + i,
+ in_params.volume);
+ }
+ ApplyGainWithoutDelta(GetMixBuffer(in_params.buffer_offset + i),
+ GetMixBuffer(in_params.buffer_offset + i), gain,
+ worker_params.sample_count);
+ }
+}
+
+s32 CommandGenerator::DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
+ s32 sample_count, s32 channel, std::size_t mix_offset) {
+ const auto& in_params = voice_info.GetInParams();
+ const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
+ if (wave_buffer.buffer_address == 0) {
+ return 0;
+ }
+ if (wave_buffer.buffer_size == 0) {
+ return 0;
+ }
+ if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
+ return 0;
+ }
+ const auto samples_remaining =
+ (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
+ const auto start_offset =
+ ((wave_buffer.start_sample_offset + dsp_state.offset) * in_params.channel_count) *
+ sizeof(s16);
+ const auto buffer_pos = wave_buffer.buffer_address + start_offset;
+ const auto samples_processed = std::min(sample_count, samples_remaining);
+
+ if (in_params.channel_count == 1) {
+ std::vector<s16> buffer(samples_processed);
+ memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
+ for (std::size_t i = 0; i < buffer.size(); i++) {
+ sample_buffer[mix_offset + i] = buffer[i];
+ }
+ } else {
+ const auto channel_count = in_params.channel_count;
+ std::vector<s16> buffer(samples_processed * channel_count);
+ memory.ReadBlock(buffer_pos, buffer.data(), buffer.size() * sizeof(s16));
+
+ for (std::size_t i = 0; i < static_cast<std::size_t>(samples_processed); i++) {
+ sample_buffer[mix_offset + i] = buffer[i * channel_count + channel];
+ }
+ }
+
+ return samples_processed;
+}
+
+s32 CommandGenerator::DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
+ s32 sample_count, s32 channel, std::size_t mix_offset) {
+ const auto& in_params = voice_info.GetInParams();
+ const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
+ if (wave_buffer.buffer_address == 0) {
+ return 0;
+ }
+ if (wave_buffer.buffer_size == 0) {
+ return 0;
+ }
+ if (wave_buffer.end_sample_offset < wave_buffer.start_sample_offset) {
+ return 0;
+ }
+
+ static constexpr std::array<int, 16> SIGNED_NIBBLES{
+ 0, 1, 2, 3, 4, 5, 6, 7, -8, -7, -6, -5, -4, -3, -2, -1,
+ };
+
+ constexpr std::size_t FRAME_LEN = 8;
+ constexpr std::size_t NIBBLES_PER_SAMPLE = 16;
+ constexpr std::size_t SAMPLES_PER_FRAME = 14;
+
+ auto frame_header = dsp_state.context.header;
+ s32 idx = (frame_header >> 4) & 0xf;
+ s32 scale = frame_header & 0xf;
+ s16 yn1 = dsp_state.context.yn1;
+ s16 yn2 = dsp_state.context.yn2;
+
+ Codec::ADPCM_Coeff coeffs;
+ memory.ReadBlock(in_params.additional_params_address, coeffs.data(),
+ sizeof(Codec::ADPCM_Coeff));
+
+ s32 coef1 = coeffs[idx * 2];
+ s32 coef2 = coeffs[idx * 2 + 1];
+
+ const auto samples_remaining =
+ (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) - dsp_state.offset;
+ const auto samples_processed = std::min(sample_count, samples_remaining);
+ const auto sample_pos = wave_buffer.start_sample_offset + dsp_state.offset;
+
+ const auto samples_remaining_in_frame = sample_pos % SAMPLES_PER_FRAME;
+ auto position_in_frame = ((sample_pos / SAMPLES_PER_FRAME) * NIBBLES_PER_SAMPLE) +
+ samples_remaining_in_frame + (samples_remaining_in_frame != 0 ? 2 : 0);
+
+ const auto decode_sample = [&](const int nibble) -> s16 {
+ const int xn = nibble * (1 << scale);
+ // We first transform everything into 11 bit fixed point, perform the second order
+ // digital filter, then transform back.
+ // 0x400 == 0.5 in 11 bit fixed point.
+ // Filter: y[n] = x[n] + 0.5 + c1 * y[n-1] + c2 * y[n-2]
+ int val = ((xn << 11) + 0x400 + coef1 * yn1 + coef2 * yn2) >> 11;
+ // Clamp to output range.
+ val = std::clamp<s32>(val, -32768, 32767);
+ // Advance output feedback.
+ yn2 = yn1;
+ yn1 = static_cast<s16>(val);
+ return yn1;
+ };
+
+ std::size_t buffer_offset{};
+ std::vector<u8> buffer(
+ std::max((samples_processed / FRAME_LEN) * SAMPLES_PER_FRAME, FRAME_LEN));
+ memory.ReadBlock(wave_buffer.buffer_address + (position_in_frame / 2), buffer.data(),
+ buffer.size());
+ std::size_t cur_mix_offset = mix_offset;
+
+ auto remaining_samples = samples_processed;
+ while (remaining_samples > 0) {
+ if (position_in_frame % NIBBLES_PER_SAMPLE == 0) {
+ // Read header
+ frame_header = buffer[buffer_offset++];
+ idx = (frame_header >> 4) & 0xf;
+ scale = frame_header & 0xf;
+ coef1 = coeffs[idx * 2];
+ coef2 = coeffs[idx * 2 + 1];
+ position_in_frame += 2;
+
+ // Decode entire frame
+ if (remaining_samples >= static_cast<int>(SAMPLES_PER_FRAME)) {
+ for (std::size_t i = 0; i < SAMPLES_PER_FRAME / 2; i++) {
+ // Sample 1
+ const s32 s0 = SIGNED_NIBBLES[buffer[buffer_offset] >> 4];
+ const s32 s1 = SIGNED_NIBBLES[buffer[buffer_offset++] & 0xf];
+ const s16 sample_1 = decode_sample(s0);
+ const s16 sample_2 = decode_sample(s1);
+ sample_buffer[cur_mix_offset++] = sample_1;
+ sample_buffer[cur_mix_offset++] = sample_2;
+ }
+ remaining_samples -= static_cast<int>(SAMPLES_PER_FRAME);
+ position_in_frame += SAMPLES_PER_FRAME;
+ continue;
+ }
+ }
+ // Decode mid frame
+ s32 current_nibble = buffer[buffer_offset];
+ if (position_in_frame++ & 0x1) {
+ current_nibble &= 0xf;
+ buffer_offset++;
+ } else {
+ current_nibble >>= 4;
+ }
+ const s16 sample = decode_sample(SIGNED_NIBBLES[current_nibble]);
+ sample_buffer[cur_mix_offset++] = sample;
+ remaining_samples--;
+ }
+
+ dsp_state.context.header = frame_header;
+ dsp_state.context.yn1 = yn1;
+ dsp_state.context.yn2 = yn2;
+
+ return samples_processed;
+}
+
+s32* CommandGenerator::GetMixBuffer(std::size_t index) {
+ return mix_buffer.data() + (index * worker_params.sample_count);
+}
+
+const s32* CommandGenerator::GetMixBuffer(std::size_t index) const {
+ return mix_buffer.data() + (index * worker_params.sample_count);
+}
+
+std::size_t CommandGenerator::GetMixChannelBufferOffset(s32 channel) const {
+ return worker_params.mix_buffer_count + channel;
+}
+
+std::size_t CommandGenerator::GetTotalMixBufferCount() const {
+ return worker_params.mix_buffer_count + AudioCommon::MAX_CHANNEL_COUNT;
+}
+
+s32* CommandGenerator::GetChannelMixBuffer(s32 channel) {
+ return GetMixBuffer(worker_params.mix_buffer_count + channel);
+}
+
+const s32* CommandGenerator::GetChannelMixBuffer(s32 channel) const {
+ return GetMixBuffer(worker_params.mix_buffer_count + channel);
+}
+
+void CommandGenerator::DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output,
+ VoiceState& dsp_state, s32 channel,
+ s32 target_sample_rate, s32 sample_count,
+ s32 node_id) {
+ const auto& in_params = voice_info.GetInParams();
+ if (dumping_frame) {
+ LOG_DEBUG(Audio,
+ "(DSP_TRACE) DecodeFromWaveBuffers, node_id={}, channel={}, "
+ "format={}, sample_count={}, sample_rate={}, mix_id={}, splitter_id={}",
+ node_id, channel, in_params.sample_format, sample_count, in_params.sample_rate,
+ in_params.mix_id, in_params.splitter_info_id);
+ }
+ ASSERT_OR_EXECUTE(output != nullptr, { return; });
+
+ const auto resample_rate = static_cast<s32>(
+ static_cast<float>(in_params.sample_rate) / static_cast<float>(target_sample_rate) *
+ static_cast<float>(static_cast<s32>(in_params.pitch * 32768.0f)));
+ if (dsp_state.fraction + sample_count * resample_rate >
+ static_cast<s32>(SCALED_MIX_BUFFER_SIZE - 4ULL)) {
+ return;
+ }
+
+ auto min_required_samples =
+ std::min(static_cast<s32>(SCALED_MIX_BUFFER_SIZE) - dsp_state.fraction, resample_rate);
+ if (min_required_samples >= sample_count) {
+ min_required_samples = sample_count;
+ }
+
+ std::size_t temp_mix_offset{};
+ bool is_buffer_completed{false};
+ auto samples_remaining = sample_count;
+ while (samples_remaining > 0 && !is_buffer_completed) {
+ const auto samples_to_output = std::min(samples_remaining, min_required_samples);
+ const auto samples_to_read = (samples_to_output * resample_rate + dsp_state.fraction) >> 15;
+
+ if (!in_params.behavior_flags.is_pitch_and_src_skipped) {
+ // Append sample histtory for resampler
+ for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
+ sample_buffer[temp_mix_offset + i] = dsp_state.sample_history[i];
+ }
+ temp_mix_offset += 4;
+ }
+
+ s32 samples_read{};
+ while (samples_read < samples_to_read) {
+ const auto& wave_buffer = in_params.wave_buffer[dsp_state.wave_buffer_index];
+ // No more data can be read
+ if (!dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index]) {
+ is_buffer_completed = true;
+ break;
+ }
+
+ if (in_params.sample_format == SampleFormat::Adpcm && dsp_state.offset == 0 &&
+ wave_buffer.context_address != 0 && wave_buffer.context_size != 0) {
+ // TODO(ogniK): ADPCM loop context
+ }
+
+ s32 samples_decoded{0};
+ switch (in_params.sample_format) {
+ case SampleFormat::Pcm16:
+ samples_decoded = DecodePcm16(voice_info, dsp_state, samples_to_read - samples_read,
+ channel, temp_mix_offset);
+ break;
+ case SampleFormat::Adpcm:
+ samples_decoded = DecodeAdpcm(voice_info, dsp_state, samples_to_read - samples_read,
+ channel, temp_mix_offset);
+ break;
+ default:
+ UNREACHABLE_MSG("Unimplemented sample format={}", in_params.sample_format);
+ }
+
+ temp_mix_offset += samples_decoded;
+ samples_read += samples_decoded;
+ dsp_state.offset += samples_decoded;
+ dsp_state.played_sample_count += samples_decoded;
+
+ if (dsp_state.offset >=
+ (wave_buffer.end_sample_offset - wave_buffer.start_sample_offset) ||
+ samples_decoded == 0) {
+ // Reset our sample offset
+ dsp_state.offset = 0;
+ if (wave_buffer.is_looping) {
+ if (samples_decoded == 0) {
+ // End of our buffer
+ is_buffer_completed = true;
+ break;
+ }
+
+ if (in_params.behavior_flags.is_played_samples_reset_at_loop_point.Value()) {
+ dsp_state.played_sample_count = 0;
+ }
+ } else {
+
+ // Update our wave buffer states
+ dsp_state.is_wave_buffer_valid[dsp_state.wave_buffer_index] = false;
+ dsp_state.wave_buffer_consumed++;
+ dsp_state.wave_buffer_index =
+ (dsp_state.wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
+ if (wave_buffer.end_of_stream) {
+ dsp_state.played_sample_count = 0;
+ }
+ }
+ }
+ }
+
+ if (in_params.behavior_flags.is_pitch_and_src_skipped.Value()) {
+ // No need to resample
+ std::memcpy(output, sample_buffer.data(), samples_read * sizeof(s32));
+ } else {
+ std::fill(sample_buffer.begin() + temp_mix_offset,
+ sample_buffer.begin() + temp_mix_offset + (samples_to_read - samples_read),
+ 0);
+ AudioCore::Resample(output, sample_buffer.data(), resample_rate, dsp_state.fraction,
+ samples_to_output);
+ // Resample
+ for (std::size_t i = 0; i < AudioCommon::MAX_SAMPLE_HISTORY; i++) {
+ dsp_state.sample_history[i] = sample_buffer[samples_to_read + i];
+ }
+ }
+ output += samples_to_output;
+ samples_remaining -= samples_to_output;
+ }
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/command_generator.h b/src/audio_core/command_generator.h
new file mode 100644
index 000000000..53e57748b
--- /dev/null
+++ b/src/audio_core/command_generator.h
@@ -0,0 +1,102 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "audio_core/common.h"
+#include "audio_core/voice_context.h"
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace AudioCore {
+class MixContext;
+class SplitterContext;
+class ServerSplitterDestinationData;
+class ServerMixInfo;
+class EffectContext;
+class EffectBase;
+struct AuxInfoDSP;
+using MixVolumeBuffer = std::array<float, AudioCommon::MAX_MIX_BUFFERS>;
+
+class CommandGenerator {
+public:
+ explicit CommandGenerator(AudioCommon::AudioRendererParameter& worker_params,
+ VoiceContext& voice_context, MixContext& mix_context,
+ SplitterContext& splitter_context, EffectContext& effect_context,
+ Core::Memory::Memory& memory);
+ ~CommandGenerator();
+
+ void ClearMixBuffers();
+ void GenerateVoiceCommands();
+ void GenerateVoiceCommand(ServerVoiceInfo& voice_info);
+ void GenerateSubMixCommands();
+ void GenerateFinalMixCommands();
+ void PreCommand();
+ void PostCommand();
+
+ s32* GetChannelMixBuffer(s32 channel);
+ const s32* GetChannelMixBuffer(s32 channel) const;
+ s32* GetMixBuffer(std::size_t index);
+ const s32* GetMixBuffer(std::size_t index) const;
+ std::size_t GetMixChannelBufferOffset(s32 channel) const;
+
+ std::size_t GetTotalMixBufferCount() const;
+
+private:
+ void GenerateDataSourceCommand(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 channel);
+ void GenerateBiquadFilterCommandForVoice(ServerVoiceInfo& voice_info, VoiceState& dsp_state,
+ s32 mix_buffer_count, s32 channel);
+ void GenerateVolumeRampCommand(float last_volume, float current_volume, s32 channel,
+ s32 node_id);
+ void GenerateVoiceMixCommand(const MixVolumeBuffer& mix_volumes,
+ const MixVolumeBuffer& last_mix_volumes, VoiceState& dsp_state,
+ s32 mix_buffer_offset, s32 mix_buffer_count, s32 voice_index,
+ s32 node_id);
+ void GenerateSubMixCommand(ServerMixInfo& mix_info);
+ void GenerateMixCommands(ServerMixInfo& mix_info);
+ void GenerateMixCommand(std::size_t output_offset, std::size_t input_offset, float volume,
+ s32 node_id);
+ void GenerateFinalMixCommand();
+ void GenerateBiquadFilterCommand(s32 mix_buffer, const BiquadFilterParameter& params,
+ std::array<s64, 2>& state, std::size_t input_offset,
+ std::size_t output_offset, s32 sample_count, s32 node_id);
+ void GenerateDepopPrepareCommand(VoiceState& dsp_state, std::size_t mix_buffer_count,
+ std::size_t mix_buffer_offset);
+ void GenerateDepopForMixBuffersCommand(std::size_t mix_buffer_count,
+ std::size_t mix_buffer_offset, s32 sample_rate);
+ void GenerateEffectCommand(ServerMixInfo& mix_info);
+ void GenerateI3dl2ReverbEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
+ void GenerateBiquadFilterEffectCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
+ void GenerateAuxCommand(s32 mix_buffer_offset, EffectBase* info, bool enabled);
+ ServerSplitterDestinationData* GetDestinationData(s32 splitter_id, s32 index);
+
+ s32 WriteAuxBuffer(AuxInfoDSP& dsp_info, VAddr send_buffer, u32 max_samples, const s32* data,
+ u32 sample_count, u32 write_offset, u32 write_count);
+ s32 ReadAuxBuffer(AuxInfoDSP& recv_info, VAddr recv_buffer, u32 max_samples, s32* out_data,
+ u32 sample_count, u32 read_offset, u32 read_count);
+
+ // DSP Code
+ s32 DecodePcm16(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
+ s32 channel, std::size_t mix_offset);
+ s32 DecodeAdpcm(ServerVoiceInfo& voice_info, VoiceState& dsp_state, s32 sample_count,
+ s32 channel, std::size_t mix_offset);
+ void DecodeFromWaveBuffers(ServerVoiceInfo& voice_info, s32* output, VoiceState& dsp_state,
+ s32 channel, s32 target_sample_rate, s32 sample_count, s32 node_id);
+
+ AudioCommon::AudioRendererParameter& worker_params;
+ VoiceContext& voice_context;
+ MixContext& mix_context;
+ SplitterContext& splitter_context;
+ EffectContext& effect_context;
+ Core::Memory::Memory& memory;
+ std::vector<s32> mix_buffer{};
+ std::vector<s32> sample_buffer{};
+ std::vector<s32> depop_buffer{};
+ bool dumping_frame{false};
+};
+} // namespace AudioCore
diff --git a/src/audio_core/common.h b/src/audio_core/common.h
index 7bb145c53..7b4a1e9e8 100644
--- a/src/audio_core/common.h
+++ b/src/audio_core/common.h
@@ -3,18 +3,36 @@
// Refer to the license.txt file included.
#pragma once
+
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
#include "core/hle/result.h"
-namespace AudioCore {
+namespace AudioCommon {
namespace Audren {
constexpr ResultCode ERR_INVALID_PARAMETERS{ErrorModule::Audio, 41};
-}
+constexpr ResultCode ERR_SPLITTER_SORT_FAILED{ErrorModule::Audio, 43};
+} // namespace Audren
constexpr u32_le CURRENT_PROCESS_REVISION = Common::MakeMagic('R', 'E', 'V', '8');
constexpr std::size_t MAX_MIX_BUFFERS = 24;
+constexpr std::size_t MAX_BIQUAD_FILTERS = 2;
+constexpr std::size_t MAX_CHANNEL_COUNT = 6;
+constexpr std::size_t MAX_WAVE_BUFFERS = 4;
+constexpr std::size_t MAX_SAMPLE_HISTORY = 4;
+constexpr u32 STREAM_SAMPLE_RATE = 48000;
+constexpr u32 STREAM_NUM_CHANNELS = 6;
+constexpr s32 NO_SPLITTER = -1;
+constexpr s32 NO_MIX = 0x7fffffff;
+constexpr s32 NO_FINAL_MIX = std::numeric_limits<s32>::min();
+constexpr s32 FINAL_MIX = 0;
+constexpr s32 NO_EFFECT_ORDER = -1;
+constexpr std::size_t TEMP_MIX_BASE_SIZE = 0x3f00; // TODO(ogniK): Work out this constant
+// Any size checks seem to take the sample history into account
+// and our const ends up being 0x3f04, the 4 bytes are most
+// likely the sample history
+constexpr std::size_t TOTAL_TEMP_MIX_SIZE = TEMP_MIX_BASE_SIZE + AudioCommon::MAX_SAMPLE_HISTORY;
static constexpr u32 VersionFromRevision(u32_le rev) {
// "REV7" -> 7
@@ -45,4 +63,46 @@ static constexpr bool CanConsumeBuffer(std::size_t size, std::size_t offset, std
return true;
}
-} // namespace AudioCore
+struct UpdateDataSizes {
+ u32_le behavior{};
+ u32_le memory_pool{};
+ u32_le voice{};
+ u32_le voice_channel_resource{};
+ u32_le effect{};
+ u32_le mixer{};
+ u32_le sink{};
+ u32_le performance{};
+ u32_le splitter{};
+ u32_le render_info{};
+ INSERT_PADDING_WORDS(4);
+};
+static_assert(sizeof(UpdateDataSizes) == 0x38, "UpdateDataSizes is an invalid size");
+
+struct UpdateDataHeader {
+ u32_le revision{};
+ UpdateDataSizes size{};
+ u32_le total_size{};
+};
+static_assert(sizeof(UpdateDataHeader) == 0x40, "UpdateDataHeader is an invalid size");
+
+struct AudioRendererParameter {
+ u32_le sample_rate;
+ u32_le sample_count;
+ u32_le mix_buffer_count;
+ u32_le submix_count;
+ u32_le voice_count;
+ u32_le sink_count;
+ u32_le effect_count;
+ u32_le performance_frame_count;
+ u8 is_voice_drop_enabled;
+ u8 unknown_21;
+ u8 unknown_22;
+ u8 execution_mode;
+ u32_le splitter_count;
+ u32_le num_splitter_send_channels;
+ u32_le unknown_30;
+ u32_le revision;
+};
+static_assert(sizeof(AudioRendererParameter) == 52, "AudioRendererParameter is an invalid size");
+
+} // namespace AudioCommon
diff --git a/src/audio_core/cubeb_sink.cpp b/src/audio_core/cubeb_sink.cpp
index c27df946c..6eaa60815 100644
--- a/src/audio_core/cubeb_sink.cpp
+++ b/src/audio_core/cubeb_sink.cpp
@@ -23,14 +23,24 @@ class CubebSinkStream final : public SinkStream {
public:
CubebSinkStream(cubeb* ctx, u32 sample_rate, u32 num_channels_, cubeb_devid output_device,
const std::string& name)
- : ctx{ctx}, num_channels{std::min(num_channels_, 2u)}, time_stretch{sample_rate,
+ : ctx{ctx}, num_channels{std::min(num_channels_, 6u)}, time_stretch{sample_rate,
num_channels} {
cubeb_stream_params params{};
params.rate = sample_rate;
params.channels = num_channels;
params.format = CUBEB_SAMPLE_S16NE;
- params.layout = num_channels == 1 ? CUBEB_LAYOUT_MONO : CUBEB_LAYOUT_STEREO;
+ switch (num_channels) {
+ case 1:
+ params.layout = CUBEB_LAYOUT_MONO;
+ break;
+ case 2:
+ params.layout = CUBEB_LAYOUT_STEREO;
+ break;
+ case 6:
+ params.layout = CUBEB_LAYOUT_3F2_LFE;
+ break;
+ }
u32 minimum_latency{};
if (cubeb_get_min_latency(ctx, &params, &minimum_latency) != CUBEB_OK) {
@@ -83,8 +93,10 @@ public:
constexpr s32 clev{707}; // center mixing level coefficient
constexpr s32 slev{707}; // surround mixing level coefficient
- buf.push_back(left + (clev * center / 1000) + (slev * surround_left / 1000));
- buf.push_back(right + (clev * center / 1000) + (slev * surround_right / 1000));
+ buf.push_back(static_cast<s16>(left + (clev * center / 1000) +
+ (slev * surround_left / 1000)));
+ buf.push_back(static_cast<s16>(right + (clev * center / 1000) +
+ (slev * surround_right / 1000)));
}
queue.Push(buf);
return;
@@ -182,8 +194,8 @@ SinkStream& CubebSink::AcquireSinkStream(u32 sample_rate, u32 num_channels,
long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const void* input_buffer,
void* output_buffer, long num_frames) {
- CubebSinkStream* impl = static_cast<CubebSinkStream*>(user_data);
- u8* buffer = reinterpret_cast<u8*>(output_buffer);
+ auto* impl = static_cast<CubebSinkStream*>(user_data);
+ auto* buffer = static_cast<u8*>(output_buffer);
if (!impl) {
return {};
@@ -193,6 +205,7 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
const std::size_t samples_to_write = num_channels * num_frames;
std::size_t samples_written;
+ /*
if (Settings::values.enable_audio_stretching.GetValue()) {
const std::vector<s16> in{impl->queue.Pop()};
const std::size_t num_in{in.size() / num_channels};
@@ -207,7 +220,8 @@ long CubebSinkStream::DataCallback(cubeb_stream* stream, void* user_data, const
}
} else {
samples_written = impl->queue.Pop(buffer, samples_to_write);
- }
+ }*/
+ samples_written = impl->queue.Pop(buffer, samples_to_write);
if (samples_written >= num_channels) {
std::memcpy(&impl->last_frame[0], buffer + (samples_written - num_channels) * sizeof(s16),
diff --git a/src/audio_core/effect_context.cpp b/src/audio_core/effect_context.cpp
new file mode 100644
index 000000000..4d9cdf524
--- /dev/null
+++ b/src/audio_core/effect_context.cpp
@@ -0,0 +1,299 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include "audio_core/effect_context.h"
+
+namespace AudioCore {
+namespace {
+bool ValidChannelCountForEffect(s32 channel_count) {
+ return channel_count == 1 || channel_count == 2 || channel_count == 4 || channel_count == 6;
+}
+} // namespace
+
+EffectContext::EffectContext(std::size_t effect_count) : effect_count(effect_count) {
+ effects.reserve(effect_count);
+ std::generate_n(std::back_inserter(effects), effect_count,
+ [] { return std::make_unique<EffectStubbed>(); });
+}
+EffectContext::~EffectContext() = default;
+
+std::size_t EffectContext::GetCount() const {
+ return effect_count;
+}
+
+EffectBase* EffectContext::GetInfo(std::size_t i) {
+ return effects.at(i).get();
+}
+
+EffectBase* EffectContext::RetargetEffect(std::size_t i, EffectType effect) {
+ switch (effect) {
+ case EffectType::Invalid:
+ effects[i] = std::make_unique<EffectStubbed>();
+ break;
+ case EffectType::BufferMixer:
+ effects[i] = std::make_unique<EffectBufferMixer>();
+ break;
+ case EffectType::Aux:
+ effects[i] = std::make_unique<EffectAuxInfo>();
+ break;
+ case EffectType::Delay:
+ effects[i] = std::make_unique<EffectDelay>();
+ break;
+ case EffectType::Reverb:
+ effects[i] = std::make_unique<EffectReverb>();
+ break;
+ case EffectType::I3dl2Reverb:
+ effects[i] = std::make_unique<EffectI3dl2Reverb>();
+ break;
+ case EffectType::BiquadFilter:
+ effects[i] = std::make_unique<EffectBiquadFilter>();
+ break;
+ default:
+ UNREACHABLE_MSG("Unimplemented effect {}", effect);
+ effects[i] = std::make_unique<EffectStubbed>();
+ }
+ return GetInfo(i);
+}
+
+const EffectBase* EffectContext::GetInfo(std::size_t i) const {
+ return effects.at(i).get();
+}
+
+EffectStubbed::EffectStubbed() : EffectBase::EffectBase(EffectType::Invalid) {}
+EffectStubbed::~EffectStubbed() = default;
+
+void EffectStubbed::Update(EffectInfo::InParams& in_params) {}
+void EffectStubbed::UpdateForCommandGeneration() {}
+
+EffectBase::EffectBase(EffectType effect_type) : effect_type(effect_type) {}
+EffectBase::~EffectBase() = default;
+
+UsageState EffectBase::GetUsage() const {
+ return usage;
+}
+
+EffectType EffectBase::GetType() const {
+ return effect_type;
+}
+
+bool EffectBase::IsEnabled() const {
+ return enabled;
+}
+
+s32 EffectBase::GetMixID() const {
+ return mix_id;
+}
+
+s32 EffectBase::GetProcessingOrder() const {
+ return processing_order;
+}
+
+EffectI3dl2Reverb::EffectI3dl2Reverb() : EffectGeneric::EffectGeneric(EffectType::I3dl2Reverb) {}
+EffectI3dl2Reverb::~EffectI3dl2Reverb() = default;
+
+void EffectI3dl2Reverb::Update(EffectInfo::InParams& in_params) {
+ auto& internal_params = GetParams();
+ const auto* reverb_params = reinterpret_cast<I3dl2ReverbParams*>(in_params.raw.data());
+ if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
+ UNREACHABLE_MSG("Invalid reverb max channel count {}", reverb_params->max_channels);
+ return;
+ }
+
+ const auto last_status = internal_params.status;
+ mix_id = in_params.mix_id;
+ processing_order = in_params.processing_order;
+ internal_params = *reverb_params;
+ if (!ValidChannelCountForEffect(reverb_params->channel_count)) {
+ internal_params.channel_count = internal_params.max_channels;
+ }
+ enabled = in_params.is_enabled;
+ if (last_status != ParameterStatus::Updated) {
+ internal_params.status = last_status;
+ }
+
+ if (in_params.is_new || skipped) {
+ usage = UsageState::Initialized;
+ internal_params.status = ParameterStatus::Initialized;
+ skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
+ }
+}
+
+void EffectI3dl2Reverb::UpdateForCommandGeneration() {
+ if (enabled) {
+ usage = UsageState::Running;
+ } else {
+ usage = UsageState::Stopped;
+ }
+ GetParams().status = ParameterStatus::Updated;
+}
+
+EffectBiquadFilter::EffectBiquadFilter() : EffectGeneric::EffectGeneric(EffectType::BiquadFilter) {}
+EffectBiquadFilter::~EffectBiquadFilter() = default;
+
+void EffectBiquadFilter::Update(EffectInfo::InParams& in_params) {
+ auto& internal_params = GetParams();
+ const auto* biquad_params = reinterpret_cast<BiquadFilterParams*>(in_params.raw.data());
+ mix_id = in_params.mix_id;
+ processing_order = in_params.processing_order;
+ internal_params = *biquad_params;
+ enabled = in_params.is_enabled;
+}
+
+void EffectBiquadFilter::UpdateForCommandGeneration() {
+ if (enabled) {
+ usage = UsageState::Running;
+ } else {
+ usage = UsageState::Stopped;
+ }
+ GetParams().status = ParameterStatus::Updated;
+}
+
+EffectAuxInfo::EffectAuxInfo() : EffectGeneric::EffectGeneric(EffectType::Aux) {}
+EffectAuxInfo::~EffectAuxInfo() = default;
+
+void EffectAuxInfo::Update(EffectInfo::InParams& in_params) {
+ const auto* aux_params = reinterpret_cast<AuxInfo*>(in_params.raw.data());
+ mix_id = in_params.mix_id;
+ processing_order = in_params.processing_order;
+ GetParams() = *aux_params;
+ enabled = in_params.is_enabled;
+
+ if (in_params.is_new || skipped) {
+ skipped = aux_params->send_buffer_info == 0 || aux_params->return_buffer_info == 0;
+ if (skipped) {
+ return;
+ }
+
+ // There's two AuxInfos which are an identical size, the first one is managed by the cpu,
+ // the second is managed by the dsp. All we care about is managing the DSP one
+ send_info = aux_params->send_buffer_info + sizeof(AuxInfoDSP);
+ send_buffer = aux_params->send_buffer_info + (sizeof(AuxInfoDSP) * 2);
+
+ recv_info = aux_params->return_buffer_info + sizeof(AuxInfoDSP);
+ recv_buffer = aux_params->return_buffer_info + (sizeof(AuxInfoDSP) * 2);
+ }
+}
+
+void EffectAuxInfo::UpdateForCommandGeneration() {
+ if (enabled) {
+ usage = UsageState::Running;
+ } else {
+ usage = UsageState::Stopped;
+ }
+}
+
+VAddr EffectAuxInfo::GetSendInfo() const {
+ return send_info;
+}
+
+VAddr EffectAuxInfo::GetSendBuffer() const {
+ return send_buffer;
+}
+
+VAddr EffectAuxInfo::GetRecvInfo() const {
+ return recv_info;
+}
+
+VAddr EffectAuxInfo::GetRecvBuffer() const {
+ return recv_buffer;
+}
+
+EffectDelay::EffectDelay() : EffectGeneric::EffectGeneric(EffectType::Delay) {}
+EffectDelay::~EffectDelay() = default;
+
+void EffectDelay::Update(EffectInfo::InParams& in_params) {
+ const auto* delay_params = reinterpret_cast<DelayParams*>(in_params.raw.data());
+ auto& internal_params = GetParams();
+ if (!ValidChannelCountForEffect(delay_params->max_channels)) {
+ return;
+ }
+
+ const auto last_status = internal_params.status;
+ mix_id = in_params.mix_id;
+ processing_order = in_params.processing_order;
+ internal_params = *delay_params;
+ if (!ValidChannelCountForEffect(delay_params->channels)) {
+ internal_params.channels = internal_params.max_channels;
+ }
+ enabled = in_params.is_enabled;
+
+ if (last_status != ParameterStatus::Updated) {
+ internal_params.status = last_status;
+ }
+
+ if (in_params.is_new || skipped) {
+ usage = UsageState::Initialized;
+ internal_params.status = ParameterStatus::Initialized;
+ skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
+ }
+}
+
+void EffectDelay::UpdateForCommandGeneration() {
+ if (enabled) {
+ usage = UsageState::Running;
+ } else {
+ usage = UsageState::Stopped;
+ }
+ GetParams().status = ParameterStatus::Updated;
+}
+
+EffectBufferMixer::EffectBufferMixer() : EffectGeneric::EffectGeneric(EffectType::BufferMixer) {}
+EffectBufferMixer::~EffectBufferMixer() = default;
+
+void EffectBufferMixer::Update(EffectInfo::InParams& in_params) {
+ mix_id = in_params.mix_id;
+ processing_order = in_params.processing_order;
+ GetParams() = *reinterpret_cast<BufferMixerParams*>(in_params.raw.data());
+ enabled = in_params.is_enabled;
+}
+
+void EffectBufferMixer::UpdateForCommandGeneration() {
+ if (enabled) {
+ usage = UsageState::Running;
+ } else {
+ usage = UsageState::Stopped;
+ }
+}
+
+EffectReverb::EffectReverb() : EffectGeneric::EffectGeneric(EffectType::Reverb) {}
+EffectReverb::~EffectReverb() = default;
+
+void EffectReverb::Update(EffectInfo::InParams& in_params) {
+ const auto* reverb_params = reinterpret_cast<ReverbParams*>(in_params.raw.data());
+ auto& internal_params = GetParams();
+ if (!ValidChannelCountForEffect(reverb_params->max_channels)) {
+ return;
+ }
+
+ const auto last_status = internal_params.status;
+ mix_id = in_params.mix_id;
+ processing_order = in_params.processing_order;
+ internal_params = *reverb_params;
+ if (!ValidChannelCountForEffect(reverb_params->channels)) {
+ internal_params.channels = internal_params.max_channels;
+ }
+ enabled = in_params.is_enabled;
+
+ if (last_status != ParameterStatus::Updated) {
+ internal_params.status = last_status;
+ }
+
+ if (in_params.is_new || skipped) {
+ usage = UsageState::Initialized;
+ internal_params.status = ParameterStatus::Initialized;
+ skipped = in_params.buffer_address == 0 || in_params.buffer_size == 0;
+ }
+}
+
+void EffectReverb::UpdateForCommandGeneration() {
+ if (enabled) {
+ usage = UsageState::Running;
+ } else {
+ usage = UsageState::Stopped;
+ }
+ GetParams().status = ParameterStatus::Updated;
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/effect_context.h b/src/audio_core/effect_context.h
new file mode 100644
index 000000000..2c4ce53ef
--- /dev/null
+++ b/src/audio_core/effect_context.h
@@ -0,0 +1,321 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <vector>
+#include "audio_core/common.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace AudioCore {
+enum class EffectType : u8 {
+ Invalid = 0,
+ BufferMixer = 1,
+ Aux = 2,
+ Delay = 3,
+ Reverb = 4,
+ I3dl2Reverb = 5,
+ BiquadFilter = 6,
+};
+
+enum class UsageStatus : u8 {
+ Invalid = 0,
+ New = 1,
+ Initialized = 2,
+ Used = 3,
+ Removed = 4,
+};
+
+enum class UsageState {
+ Invalid = 0,
+ Initialized = 1,
+ Running = 2,
+ Stopped = 3,
+};
+
+enum class ParameterStatus : u8 {
+ Initialized = 0,
+ Updating = 1,
+ Updated = 2,
+};
+
+struct BufferMixerParams {
+ std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input{};
+ std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output{};
+ std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> volume{};
+ s32_le count{};
+};
+static_assert(sizeof(BufferMixerParams) == 0x94, "BufferMixerParams is an invalid size");
+
+struct AuxInfoDSP {
+ u32_le read_offset{};
+ u32_le write_offset{};
+ u32_le remaining{};
+ INSERT_PADDING_WORDS(13);
+};
+static_assert(sizeof(AuxInfoDSP) == 0x40, "AuxInfoDSP is an invalid size");
+
+struct AuxInfo {
+ std::array<s8, AudioCommon::MAX_MIX_BUFFERS> input_mix_buffers{};
+ std::array<s8, AudioCommon::MAX_MIX_BUFFERS> output_mix_buffers{};
+ u32_le count{};
+ s32_le sample_rate{};
+ s32_le sample_count{};
+ s32_le mix_buffer_count{};
+ u64_le send_buffer_info{};
+ u64_le send_buffer_base{};
+
+ u64_le return_buffer_info{};
+ u64_le return_buffer_base{};
+};
+static_assert(sizeof(AuxInfo) == 0x60, "AuxInfo is an invalid size");
+
+struct I3dl2ReverbParams {
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
+ u16_le max_channels{};
+ u16_le channel_count{};
+ INSERT_PADDING_BYTES(1);
+ u32_le sample_rate{};
+ f32 room_hf{};
+ f32 hf_reference{};
+ f32 decay_time{};
+ f32 hf_decay_ratio{};
+ f32 room{};
+ f32 reflection{};
+ f32 reverb{};
+ f32 diffusion{};
+ f32 reflection_delay{};
+ f32 reverb_delay{};
+ f32 density{};
+ f32 dry_gain{};
+ ParameterStatus status{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(I3dl2ReverbParams) == 0x4c, "I3dl2ReverbParams is an invalid size");
+
+struct BiquadFilterParams {
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
+ std::array<s16_le, 3> numerator;
+ std::array<s16_le, 2> denominator;
+ s8 channel_count{};
+ ParameterStatus status{};
+};
+static_assert(sizeof(BiquadFilterParams) == 0x18, "BiquadFilterParams is an invalid size");
+
+struct DelayParams {
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
+ u16_le max_channels{};
+ u16_le channels{};
+ s32_le max_delay{};
+ s32_le delay{};
+ s32_le sample_rate{};
+ s32_le gain{};
+ s32_le feedback_gain{};
+ s32_le out_gain{};
+ s32_le dry_gain{};
+ s32_le channel_spread{};
+ s32_le low_pass{};
+ ParameterStatus status{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(DelayParams) == 0x38, "DelayParams is an invalid size");
+
+struct ReverbParams {
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> input{};
+ std::array<s8, AudioCommon::MAX_CHANNEL_COUNT> output{};
+ u16_le max_channels{};
+ u16_le channels{};
+ s32_le sample_rate{};
+ s32_le mode0{};
+ s32_le mode0_gain{};
+ s32_le pre_delay{};
+ s32_le mode1{};
+ s32_le mode1_gain{};
+ s32_le decay{};
+ s32_le hf_decay_ratio{};
+ s32_le coloration{};
+ s32_le reverb_gain{};
+ s32_le out_gain{};
+ s32_le dry_gain{};
+ ParameterStatus status{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(ReverbParams) == 0x44, "ReverbParams is an invalid size");
+
+class EffectInfo {
+public:
+ struct InParams {
+ EffectType type{};
+ u8 is_new{};
+ u8 is_enabled{};
+ INSERT_PADDING_BYTES(1);
+ s32_le mix_id{};
+ u64_le buffer_address{};
+ u64_le buffer_size{};
+ s32_le processing_order{};
+ INSERT_PADDING_BYTES(4);
+ union {
+ std::array<u8, 0xa0> raw;
+ };
+ };
+ static_assert(sizeof(InParams) == 0xc0, "InParams is an invalid size");
+
+ struct OutParams {
+ UsageStatus status{};
+ INSERT_PADDING_BYTES(15);
+ };
+ static_assert(sizeof(OutParams) == 0x10, "OutParams is an invalid size");
+};
+
+struct AuxAddress {
+ VAddr send_dsp_info{};
+ VAddr send_buffer_base{};
+ VAddr return_dsp_info{};
+ VAddr return_buffer_base{};
+};
+
+class EffectBase {
+public:
+ explicit EffectBase(EffectType effect_type);
+ virtual ~EffectBase();
+
+ virtual void Update(EffectInfo::InParams& in_params) = 0;
+ virtual void UpdateForCommandGeneration() = 0;
+ UsageState GetUsage() const;
+ EffectType GetType() const;
+ bool IsEnabled() const;
+ s32 GetMixID() const;
+ s32 GetProcessingOrder() const;
+
+protected:
+ UsageState usage{UsageState::Invalid};
+ EffectType effect_type{};
+ s32 mix_id{};
+ s32 processing_order{};
+ bool enabled = false;
+};
+
+template <typename T>
+class EffectGeneric : public EffectBase {
+public:
+ explicit EffectGeneric(EffectType effect_type) : EffectBase(effect_type) {}
+
+ T& GetParams() {
+ return internal_params;
+ }
+
+ const I3dl2ReverbParams& GetParams() const {
+ return internal_params;
+ }
+
+private:
+ T internal_params{};
+};
+
+class EffectStubbed : public EffectBase {
+public:
+ explicit EffectStubbed();
+ ~EffectStubbed() override;
+
+ void Update(EffectInfo::InParams& in_params) override;
+ void UpdateForCommandGeneration() override;
+};
+
+class EffectI3dl2Reverb : public EffectGeneric<I3dl2ReverbParams> {
+public:
+ explicit EffectI3dl2Reverb();
+ ~EffectI3dl2Reverb() override;
+
+ void Update(EffectInfo::InParams& in_params) override;
+ void UpdateForCommandGeneration() override;
+
+private:
+ bool skipped = false;
+};
+
+class EffectBiquadFilter : public EffectGeneric<BiquadFilterParams> {
+public:
+ explicit EffectBiquadFilter();
+ ~EffectBiquadFilter() override;
+
+ void Update(EffectInfo::InParams& in_params) override;
+ void UpdateForCommandGeneration() override;
+};
+
+class EffectAuxInfo : public EffectGeneric<AuxInfo> {
+public:
+ explicit EffectAuxInfo();
+ ~EffectAuxInfo() override;
+
+ void Update(EffectInfo::InParams& in_params) override;
+ void UpdateForCommandGeneration() override;
+ VAddr GetSendInfo() const;
+ VAddr GetSendBuffer() const;
+ VAddr GetRecvInfo() const;
+ VAddr GetRecvBuffer() const;
+
+private:
+ VAddr send_info{};
+ VAddr send_buffer{};
+ VAddr recv_info{};
+ VAddr recv_buffer{};
+ bool skipped = false;
+ AuxAddress addresses{};
+};
+
+class EffectDelay : public EffectGeneric<DelayParams> {
+public:
+ explicit EffectDelay();
+ ~EffectDelay() override;
+
+ void Update(EffectInfo::InParams& in_params) override;
+ void UpdateForCommandGeneration() override;
+
+private:
+ bool skipped = false;
+};
+
+class EffectBufferMixer : public EffectGeneric<BufferMixerParams> {
+public:
+ explicit EffectBufferMixer();
+ ~EffectBufferMixer() override;
+
+ void Update(EffectInfo::InParams& in_params) override;
+ void UpdateForCommandGeneration() override;
+};
+
+class EffectReverb : public EffectGeneric<ReverbParams> {
+public:
+ explicit EffectReverb();
+ ~EffectReverb() override;
+
+ void Update(EffectInfo::InParams& in_params) override;
+ void UpdateForCommandGeneration() override;
+
+private:
+ bool skipped = false;
+};
+
+class EffectContext {
+public:
+ explicit EffectContext(std::size_t effect_count);
+ ~EffectContext();
+
+ std::size_t GetCount() const;
+ EffectBase* GetInfo(std::size_t i);
+ EffectBase* RetargetEffect(std::size_t i, EffectType effect);
+ const EffectBase* GetInfo(std::size_t i) const;
+
+private:
+ std::size_t effect_count{};
+ std::vector<std::unique_ptr<EffectBase>> effects;
+};
+} // namespace AudioCore
diff --git a/src/audio_core/info_updater.cpp b/src/audio_core/info_updater.cpp
new file mode 100644
index 000000000..2940e53a9
--- /dev/null
+++ b/src/audio_core/info_updater.cpp
@@ -0,0 +1,516 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/behavior_info.h"
+#include "audio_core/effect_context.h"
+#include "audio_core/info_updater.h"
+#include "audio_core/memory_pool.h"
+#include "audio_core/mix_context.h"
+#include "audio_core/sink_context.h"
+#include "audio_core/splitter_context.h"
+#include "audio_core/voice_context.h"
+#include "common/logging/log.h"
+
+namespace AudioCore {
+
+InfoUpdater::InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
+ BehaviorInfo& behavior_info)
+ : in_params(in_params), out_params(out_params), behavior_info(behavior_info) {
+ ASSERT(
+ AudioCommon::CanConsumeBuffer(in_params.size(), 0, sizeof(AudioCommon::UpdateDataHeader)));
+ std::memcpy(&input_header, in_params.data(), sizeof(AudioCommon::UpdateDataHeader));
+ output_header.total_size = sizeof(AudioCommon::UpdateDataHeader);
+}
+
+InfoUpdater::~InfoUpdater() = default;
+
+bool InfoUpdater::UpdateBehaviorInfo(BehaviorInfo& in_behavior_info) {
+ if (input_header.size.behavior != sizeof(BehaviorInfo::InParams)) {
+ LOG_ERROR(Audio, "Behavior info is an invalid size, expecting 0x{:X} but got 0x{:X}",
+ sizeof(BehaviorInfo::InParams), input_header.size.behavior);
+ return false;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset,
+ sizeof(BehaviorInfo::InParams))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ BehaviorInfo::InParams behavior_in{};
+ std::memcpy(&behavior_in, in_params.data() + input_offset, sizeof(BehaviorInfo::InParams));
+ input_offset += sizeof(BehaviorInfo::InParams);
+
+ // Make sure it's an audio revision we can actually support
+ if (!AudioCommon::IsValidRevision(behavior_in.revision)) {
+ LOG_ERROR(Audio, "Invalid input revision, revision=0x{:08X}", behavior_in.revision);
+ return false;
+ }
+
+ // Make sure that our behavior info revision matches the input
+ if (in_behavior_info.GetUserRevision() != behavior_in.revision) {
+ LOG_ERROR(Audio,
+ "User revision differs from input revision, expecting 0x{:08X} but got 0x{:08X}",
+ in_behavior_info.GetUserRevision(), behavior_in.revision);
+ return false;
+ }
+
+ // Update behavior info flags
+ in_behavior_info.ClearError();
+ in_behavior_info.UpdateFlags(behavior_in.flags);
+
+ return true;
+}
+
+bool InfoUpdater::UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info) {
+ const auto memory_pool_count = memory_pool_info.size();
+ const auto total_memory_pool_in = sizeof(ServerMemoryPoolInfo::InParams) * memory_pool_count;
+ const auto total_memory_pool_out = sizeof(ServerMemoryPoolInfo::OutParams) * memory_pool_count;
+
+ if (input_header.size.memory_pool != total_memory_pool_in) {
+ LOG_ERROR(Audio, "Memory pools are an invalid size, expecting 0x{:X} but got 0x{:X}",
+ total_memory_pool_in, input_header.size.memory_pool);
+ return false;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_memory_pool_in)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ std::vector<ServerMemoryPoolInfo::InParams> mempool_in(memory_pool_count);
+ std::vector<ServerMemoryPoolInfo::OutParams> mempool_out(memory_pool_count);
+
+ std::memcpy(mempool_in.data(), in_params.data() + input_offset, total_memory_pool_in);
+ input_offset += total_memory_pool_in;
+
+ // Update our memory pools
+ for (std::size_t i = 0; i < memory_pool_count; i++) {
+ if (!memory_pool_info[i].Update(mempool_in[i], mempool_out[i])) {
+ LOG_ERROR(Audio, "Failed to update memory pool {}!", i);
+ return false;
+ }
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset,
+ sizeof(BehaviorInfo::InParams))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ std::memcpy(out_params.data() + output_offset, mempool_out.data(), total_memory_pool_out);
+ output_offset += total_memory_pool_out;
+ output_header.size.memory_pool = static_cast<u32>(total_memory_pool_out);
+ return true;
+}
+
+bool InfoUpdater::UpdateVoiceChannelResources(VoiceContext& voice_context) {
+ const auto voice_count = voice_context.GetVoiceCount();
+ const auto voice_size = voice_count * sizeof(VoiceChannelResource::InParams);
+ std::vector<VoiceChannelResource::InParams> resources_in(voice_count);
+
+ if (input_header.size.voice_channel_resource != voice_size) {
+ LOG_ERROR(Audio, "VoiceChannelResource is an invalid size, expecting 0x{:X} but got 0x{:X}",
+ voice_size, input_header.size.voice_channel_resource);
+ return false;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, voice_size)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ std::memcpy(resources_in.data(), in_params.data() + input_offset, voice_size);
+ input_offset += voice_size;
+
+ // Update our channel resources
+ for (std::size_t i = 0; i < voice_count; i++) {
+ // Grab our channel resource
+ auto& resource = voice_context.GetChannelResource(i);
+ resource.Update(resources_in[i]);
+ }
+
+ return true;
+}
+
+bool InfoUpdater::UpdateVoices(VoiceContext& voice_context,
+ std::vector<ServerMemoryPoolInfo>& memory_pool_info,
+ VAddr audio_codec_dsp_addr) {
+ const auto voice_count = voice_context.GetVoiceCount();
+ std::vector<VoiceInfo::InParams> voice_in(voice_count);
+ std::vector<VoiceInfo::OutParams> voice_out(voice_count);
+
+ const auto voice_in_size = voice_count * sizeof(VoiceInfo::InParams);
+ const auto voice_out_size = voice_count * sizeof(VoiceInfo::OutParams);
+
+ if (input_header.size.voice != voice_in_size) {
+ LOG_ERROR(Audio, "Voices are an invalid size, expecting 0x{:X} but got 0x{:X}",
+ voice_in_size, input_header.size.voice);
+ return false;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, voice_in_size)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ std::memcpy(voice_in.data(), in_params.data() + input_offset, voice_in_size);
+ input_offset += voice_in_size;
+
+ // Set all voices to not be in use
+ for (std::size_t i = 0; i < voice_count; i++) {
+ voice_context.GetInfo(i).GetInParams().in_use = false;
+ }
+
+ // Update our voices
+ for (std::size_t i = 0; i < voice_count; i++) {
+ auto& in_params = voice_in[i];
+ const auto channel_count = static_cast<std::size_t>(in_params.channel_count);
+ // Skip if it's not currently in use
+ if (!in_params.is_in_use) {
+ continue;
+ }
+ // Voice states for each channel
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> voice_states{};
+ ASSERT(static_cast<std::size_t>(in_params.id) < voice_count);
+
+ // Grab our current voice info
+ auto& voice_info = voice_context.GetInfo(static_cast<std::size_t>(in_params.id));
+
+ ASSERT(channel_count <= AudioCommon::MAX_CHANNEL_COUNT);
+
+ // Get all our channel voice states
+ for (std::size_t channel = 0; channel < channel_count; channel++) {
+ voice_states[channel] =
+ &voice_context.GetState(in_params.voice_channel_resource_ids[channel]);
+ }
+
+ if (in_params.is_new) {
+ // Default our values for our voice
+ voice_info.Initialize();
+ if (channel_count == 0 || channel_count > AudioCommon::MAX_CHANNEL_COUNT) {
+ continue;
+ }
+
+ // Zero out our voice states
+ for (std::size_t channel = 0; channel < channel_count; channel++) {
+ std::memset(voice_states[channel], 0, sizeof(VoiceState));
+ }
+ }
+
+ // Update our voice
+ voice_info.UpdateParameters(in_params, behavior_info);
+ // TODO(ogniK): Handle mapping errors with behavior info based on in params response
+
+ // Update our wave buffers
+ voice_info.UpdateWaveBuffers(in_params, voice_states, behavior_info);
+ voice_info.WriteOutStatus(voice_out[i], in_params, voice_states);
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, voice_out_size)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+ std::memcpy(out_params.data() + output_offset, voice_out.data(), voice_out_size);
+ output_offset += voice_out_size;
+ output_header.size.voice = static_cast<u32>(voice_out_size);
+ return true;
+}
+
+bool InfoUpdater::UpdateEffects(EffectContext& effect_context, bool is_active) {
+ const auto effect_count = effect_context.GetCount();
+ std::vector<EffectInfo::InParams> effect_in(effect_count);
+ std::vector<EffectInfo::OutParams> effect_out(effect_count);
+
+ const auto total_effect_in = effect_count * sizeof(EffectInfo::InParams);
+ const auto total_effect_out = effect_count * sizeof(EffectInfo::OutParams);
+
+ if (input_header.size.effect != total_effect_in) {
+ LOG_ERROR(Audio, "Effects are an invalid size, expecting 0x{:X} but got 0x{:X}",
+ total_effect_in, input_header.size.effect);
+ return false;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_effect_in)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ std::memcpy(effect_in.data(), in_params.data() + input_offset, total_effect_in);
+ input_offset += total_effect_in;
+
+ // Update effects
+ for (std::size_t i = 0; i < effect_count; i++) {
+ auto* info = effect_context.GetInfo(i);
+ if (effect_in[i].type != info->GetType()) {
+ info = effect_context.RetargetEffect(i, effect_in[i].type);
+ }
+
+ info->Update(effect_in[i]);
+
+ if ((!is_active && info->GetUsage() != UsageState::Initialized) ||
+ info->GetUsage() == UsageState::Stopped) {
+ effect_out[i].status = UsageStatus::Removed;
+ } else {
+ effect_out[i].status = UsageStatus::Used;
+ }
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_effect_out)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ std::memcpy(out_params.data() + output_offset, effect_out.data(), total_effect_out);
+ output_offset += total_effect_out;
+ output_header.size.effect = static_cast<u32>(total_effect_out);
+
+ return true;
+}
+
+bool InfoUpdater::UpdateSplitterInfo(SplitterContext& splitter_context) {
+ std::size_t start_offset = input_offset;
+ std::size_t bytes_read{};
+ // Update splitter context
+ if (!splitter_context.Update(in_params, input_offset, bytes_read)) {
+ LOG_ERROR(Audio, "Failed to update splitter context!");
+ return false;
+ }
+
+ const auto consumed = input_offset - start_offset;
+
+ if (input_header.size.splitter != consumed) {
+ LOG_ERROR(Audio, "Splitters is an invalid size, expecting 0x{:X} but got 0x{:X}",
+ bytes_read, input_header.size.splitter);
+ return false;
+ }
+
+ return true;
+}
+
+ResultCode InfoUpdater::UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
+ SplitterContext& splitter_context,
+ EffectContext& effect_context) {
+ std::vector<MixInfo::InParams> mix_in_params;
+
+ if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
+ // If we're not dirty, get ALL mix in parameters
+ const auto context_mix_count = mix_context.GetCount();
+ const auto total_mix_in = context_mix_count * sizeof(MixInfo::InParams);
+ if (input_header.size.mixer != total_mix_in) {
+ LOG_ERROR(Audio, "Mixer is an invalid size, expecting 0x{:X} but got 0x{:X}",
+ total_mix_in, input_header.size.mixer);
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_mix_in)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
+ }
+
+ mix_in_params.resize(context_mix_count);
+ std::memcpy(mix_in_params.data(), in_params.data() + input_offset, total_mix_in);
+
+ input_offset += total_mix_in;
+ } else {
+ // Only update the "dirty" mixes
+ MixInfo::DirtyHeader dirty_header{};
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset,
+ sizeof(MixInfo::DirtyHeader))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
+ }
+
+ std::memcpy(&dirty_header, in_params.data() + input_offset, sizeof(MixInfo::DirtyHeader));
+ input_offset += sizeof(MixInfo::DirtyHeader);
+
+ const auto total_mix_in =
+ dirty_header.mixer_count * sizeof(MixInfo::InParams) + sizeof(MixInfo::DirtyHeader);
+
+ if (input_header.size.mixer != total_mix_in) {
+ LOG_ERROR(Audio, "Mixer is an invalid size, expecting 0x{:X} but got 0x{:X}",
+ total_mix_in, input_header.size.mixer);
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
+ }
+
+ if (dirty_header.mixer_count != 0) {
+ mix_in_params.resize(dirty_header.mixer_count);
+ std::memcpy(mix_in_params.data(), in_params.data() + input_offset,
+ mix_in_params.size() * sizeof(MixInfo::InParams));
+ input_offset += mix_in_params.size() * sizeof(MixInfo::InParams);
+ }
+ }
+
+ // Get our total input count
+ const auto mix_count = mix_in_params.size();
+
+ if (!behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
+ // Only verify our buffer count if we're not dirty
+ std::size_t total_buffer_count{};
+ for (std::size_t i = 0; i < mix_count; i++) {
+ const auto& in = mix_in_params[i];
+ total_buffer_count += in.buffer_count;
+ if (static_cast<std::size_t>(in.dest_mix_id) > mix_count &&
+ in.dest_mix_id != AudioCommon::NO_MIX && in.mix_id != AudioCommon::FINAL_MIX) {
+ LOG_ERROR(
+ Audio,
+ "Invalid mix destination, mix_id={:X}, dest_mix_id={:X}, mix_buffer_count={:X}",
+ in.mix_id, in.dest_mix_id, mix_buffer_count);
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
+ }
+ }
+
+ if (total_buffer_count > mix_buffer_count) {
+ LOG_ERROR(Audio,
+ "Too many mix buffers used! mix_buffer_count={:X}, requesting_buffers={:X}",
+ mix_buffer_count, total_buffer_count);
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
+ }
+ }
+
+ if (mix_buffer_count == 0) {
+ LOG_ERROR(Audio, "No mix buffers!");
+ return AudioCommon::Audren::ERR_INVALID_PARAMETERS;
+ }
+
+ bool should_sort = false;
+ for (std::size_t i = 0; i < mix_count; i++) {
+ const auto& mix_in = mix_in_params[i];
+ std::size_t target_mix{};
+ if (behavior_info.IsMixInParameterDirtyOnlyUpdateSupported()) {
+ target_mix = mix_in.mix_id;
+ } else {
+ // Non dirty supported games just use i instead of the actual mix_id
+ target_mix = i;
+ }
+ auto& mix_info = mix_context.GetInfo(target_mix);
+ auto& mix_info_params = mix_info.GetInParams();
+ if (mix_info_params.in_use != mix_in.in_use) {
+ mix_info_params.in_use = mix_in.in_use;
+ mix_info.ResetEffectProcessingOrder();
+ should_sort = true;
+ }
+
+ if (mix_in.in_use) {
+ should_sort |= mix_info.Update(mix_context.GetEdgeMatrix(), mix_in, behavior_info,
+ splitter_context, effect_context);
+ }
+ }
+
+ if (should_sort && behavior_info.IsSplitterSupported()) {
+ // Sort our splitter data
+ if (!mix_context.TsortInfo(splitter_context)) {
+ return AudioCommon::Audren::ERR_SPLITTER_SORT_FAILED;
+ }
+ }
+
+ // TODO(ogniK): Sort when splitter is suppoorted
+
+ return RESULT_SUCCESS;
+}
+
+bool InfoUpdater::UpdateSinks(SinkContext& sink_context) {
+ const auto sink_count = sink_context.GetCount();
+ std::vector<SinkInfo::InParams> sink_in_params(sink_count);
+ const auto total_sink_in = sink_count * sizeof(SinkInfo::InParams);
+
+ if (input_header.size.sink != total_sink_in) {
+ LOG_ERROR(Audio, "Sinks are an invalid size, expecting 0x{:X} but got 0x{:X}",
+ total_sink_in, input_header.size.effect);
+ return false;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(in_params.size(), input_offset, total_sink_in)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ std::memcpy(sink_in_params.data(), in_params.data() + input_offset, total_sink_in);
+ input_offset += total_sink_in;
+
+ // TODO(ogniK): Properly update sinks
+ if (!sink_in_params.empty()) {
+ sink_context.UpdateMainSink(sink_in_params[0]);
+ }
+
+ output_header.size.sink = static_cast<u32>(0x20 * sink_count);
+ output_offset += 0x20 * sink_count;
+ return true;
+}
+
+bool InfoUpdater::UpdatePerformanceBuffer() {
+ output_header.size.performance = 0x10;
+ output_offset += 0x10;
+ return true;
+}
+
+bool InfoUpdater::UpdateErrorInfo(BehaviorInfo& in_behavior_info) {
+ const auto total_beahvior_info_out = sizeof(BehaviorInfo::OutParams);
+
+ if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_beahvior_info_out)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+
+ BehaviorInfo::OutParams behavior_info_out{};
+ behavior_info.CopyErrorInfo(behavior_info_out);
+
+ std::memcpy(out_params.data() + output_offset, &behavior_info_out, total_beahvior_info_out);
+ output_offset += total_beahvior_info_out;
+ output_header.size.behavior = total_beahvior_info_out;
+
+ return true;
+}
+
+struct RendererInfo {
+ u64_le elasped_frame_count{};
+ INSERT_PADDING_WORDS(2);
+};
+static_assert(sizeof(RendererInfo) == 0x10, "RendererInfo is an invalid size");
+
+bool InfoUpdater::UpdateRendererInfo(std::size_t elapsed_frame_count) {
+ const auto total_renderer_info_out = sizeof(RendererInfo);
+ if (!AudioCommon::CanConsumeBuffer(out_params.size(), output_offset, total_renderer_info_out)) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+ RendererInfo out{};
+ out.elasped_frame_count = elapsed_frame_count;
+ std::memcpy(out_params.data() + output_offset, &out, total_renderer_info_out);
+ output_offset += total_renderer_info_out;
+ output_header.size.render_info = total_renderer_info_out;
+
+ return true;
+}
+
+bool InfoUpdater::CheckConsumedSize() const {
+ if (output_offset != out_params.size()) {
+ LOG_ERROR(Audio, "Output is not consumed! Consumed {}, but requires {}. {} bytes remaining",
+ output_offset, out_params.size(), out_params.size() - output_offset);
+ return false;
+ }
+ /*if (input_offset != in_params.size()) {
+ LOG_ERROR(Audio, "Input is not consumed!");
+ return false;
+ }*/
+ return true;
+}
+
+bool InfoUpdater::WriteOutputHeader() {
+ if (!AudioCommon::CanConsumeBuffer(out_params.size(), 0,
+ sizeof(AudioCommon::UpdateDataHeader))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+ output_header.revision = AudioCommon::CURRENT_PROCESS_REVISION;
+ const auto& sz = output_header.size;
+ output_header.total_size += sz.behavior + sz.memory_pool + sz.voice +
+ sz.voice_channel_resource + sz.effect + sz.mixer + sz.sink +
+ sz.performance + sz.splitter + sz.render_info;
+
+ std::memcpy(out_params.data(), &output_header, sizeof(AudioCommon::UpdateDataHeader));
+ return true;
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/info_updater.h b/src/audio_core/info_updater.h
new file mode 100644
index 000000000..06f9d770f
--- /dev/null
+++ b/src/audio_core/info_updater.h
@@ -0,0 +1,58 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include "audio_core/common.h"
+#include "common/common_types.h"
+
+namespace AudioCore {
+
+class BehaviorInfo;
+class ServerMemoryPoolInfo;
+class VoiceContext;
+class EffectContext;
+class MixContext;
+class SinkContext;
+class SplitterContext;
+
+class InfoUpdater {
+public:
+ // TODO(ogniK): Pass process handle when we support it
+ InfoUpdater(const std::vector<u8>& in_params, std::vector<u8>& out_params,
+ BehaviorInfo& behavior_info);
+ ~InfoUpdater();
+
+ bool UpdateBehaviorInfo(BehaviorInfo& in_behavior_info);
+ bool UpdateMemoryPools(std::vector<ServerMemoryPoolInfo>& memory_pool_info);
+ bool UpdateVoiceChannelResources(VoiceContext& voice_context);
+ bool UpdateVoices(VoiceContext& voice_context,
+ std::vector<ServerMemoryPoolInfo>& memory_pool_info,
+ VAddr audio_codec_dsp_addr);
+ bool UpdateEffects(EffectContext& effect_context, bool is_active);
+ bool UpdateSplitterInfo(SplitterContext& splitter_context);
+ ResultCode UpdateMixes(MixContext& mix_context, std::size_t mix_buffer_count,
+ SplitterContext& splitter_context, EffectContext& effect_context);
+ bool UpdateSinks(SinkContext& sink_context);
+ bool UpdatePerformanceBuffer();
+ bool UpdateErrorInfo(BehaviorInfo& in_behavior_info);
+ bool UpdateRendererInfo(std::size_t elapsed_frame_count);
+ bool CheckConsumedSize() const;
+
+ bool WriteOutputHeader();
+
+private:
+ const std::vector<u8>& in_params;
+ std::vector<u8>& out_params;
+ BehaviorInfo& behavior_info;
+
+ AudioCommon::UpdateDataHeader input_header{};
+ AudioCommon::UpdateDataHeader output_header{};
+
+ std::size_t input_offset{sizeof(AudioCommon::UpdateDataHeader)};
+ std::size_t output_offset{sizeof(AudioCommon::UpdateDataHeader)};
+};
+
+} // namespace AudioCore
diff --git a/src/audio_core/memory_pool.cpp b/src/audio_core/memory_pool.cpp
new file mode 100644
index 000000000..5a3453063
--- /dev/null
+++ b/src/audio_core/memory_pool.cpp
@@ -0,0 +1,62 @@
+
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/memory_pool.h"
+#include "common/logging/log.h"
+
+namespace AudioCore {
+
+ServerMemoryPoolInfo::ServerMemoryPoolInfo() = default;
+ServerMemoryPoolInfo::~ServerMemoryPoolInfo() = default;
+bool ServerMemoryPoolInfo::Update(const ServerMemoryPoolInfo::InParams& in_params,
+ ServerMemoryPoolInfo::OutParams& out_params) {
+ // Our state does not need to be changed
+ if (in_params.state != ServerMemoryPoolInfo::State::RequestAttach &&
+ in_params.state != ServerMemoryPoolInfo::State::RequestDetach) {
+ return true;
+ }
+
+ // Address or size is null
+ if (in_params.address == 0 || in_params.size == 0) {
+ LOG_ERROR(Audio, "Memory pool address or size is zero! address={:X}, size={:X}",
+ in_params.address, in_params.size);
+ return false;
+ }
+
+ // Address or size is not aligned
+ if ((in_params.address % 0x1000) != 0 || (in_params.size % 0x1000) != 0) {
+ LOG_ERROR(Audio, "Memory pool address or size is not aligned! address={:X}, size={:X}",
+ in_params.address, in_params.size);
+ return false;
+ }
+
+ if (in_params.state == ServerMemoryPoolInfo::State::RequestAttach) {
+ cpu_address = in_params.address;
+ size = in_params.size;
+ used = true;
+ out_params.state = ServerMemoryPoolInfo::State::Attached;
+ } else {
+ // Unexpected address
+ if (cpu_address != in_params.address) {
+ LOG_ERROR(Audio, "Memory pool address differs! Expecting {:X} but address is {:X}",
+ cpu_address, in_params.address);
+ return false;
+ }
+
+ if (size != in_params.size) {
+ LOG_ERROR(Audio, "Memory pool size differs! Expecting {:X} but size is {:X}", size,
+ in_params.size);
+ return false;
+ }
+
+ cpu_address = 0;
+ size = 0;
+ used = false;
+ out_params.state = ServerMemoryPoolInfo::State::Detached;
+ }
+ return true;
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/memory_pool.h b/src/audio_core/memory_pool.h
new file mode 100644
index 000000000..8ac503f1c
--- /dev/null
+++ b/src/audio_core/memory_pool.h
@@ -0,0 +1,53 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace AudioCore {
+
+class ServerMemoryPoolInfo {
+public:
+ ServerMemoryPoolInfo();
+ ~ServerMemoryPoolInfo();
+
+ enum class State : u32_le {
+ Invalid = 0x0,
+ Aquired = 0x1,
+ RequestDetach = 0x2,
+ Detached = 0x3,
+ RequestAttach = 0x4,
+ Attached = 0x5,
+ Released = 0x6,
+ };
+
+ struct InParams {
+ u64_le address{};
+ u64_le size{};
+ ServerMemoryPoolInfo::State state{};
+ INSERT_PADDING_WORDS(3);
+ };
+ static_assert(sizeof(ServerMemoryPoolInfo::InParams) == 0x20, "InParams are an invalid size");
+
+ struct OutParams {
+ ServerMemoryPoolInfo::State state{};
+ INSERT_PADDING_WORDS(3);
+ };
+ static_assert(sizeof(ServerMemoryPoolInfo::OutParams) == 0x10, "OutParams are an invalid size");
+
+ bool Update(const ServerMemoryPoolInfo::InParams& in_params,
+ ServerMemoryPoolInfo::OutParams& out_params);
+
+private:
+ // There's another entry here which is the DSP address, however since we're not talking to the
+ // DSP we can just use the same address provided by the guest without needing to remap
+ u64_le cpu_address{};
+ u64_le size{};
+ bool used{};
+};
+
+} // namespace AudioCore
diff --git a/src/audio_core/mix_context.cpp b/src/audio_core/mix_context.cpp
new file mode 100644
index 000000000..4bca72eb0
--- /dev/null
+++ b/src/audio_core/mix_context.cpp
@@ -0,0 +1,296 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/behavior_info.h"
+#include "audio_core/common.h"
+#include "audio_core/effect_context.h"
+#include "audio_core/mix_context.h"
+#include "audio_core/splitter_context.h"
+
+namespace AudioCore {
+MixContext::MixContext() = default;
+MixContext::~MixContext() = default;
+
+void MixContext::Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
+ std::size_t effect_count) {
+ info_count = mix_count;
+ infos.resize(info_count);
+ auto& final_mix = GetInfo(AudioCommon::FINAL_MIX);
+ final_mix.GetInParams().mix_id = AudioCommon::FINAL_MIX;
+ sorted_info.reserve(infos.size());
+ for (auto& info : infos) {
+ sorted_info.push_back(&info);
+ }
+
+ for (auto& info : infos) {
+ info.SetEffectCount(effect_count);
+ }
+
+ // Only initialize our edge matrix and node states if splitters are supported
+ if (behavior_info.IsSplitterSupported()) {
+ node_states.Initialize(mix_count);
+ edge_matrix.Initialize(mix_count);
+ }
+}
+
+void MixContext::UpdateDistancesFromFinalMix() {
+ // Set all distances to be invalid
+ for (std::size_t i = 0; i < info_count; i++) {
+ GetInfo(i).GetInParams().final_mix_distance = AudioCommon::NO_FINAL_MIX;
+ }
+
+ for (std::size_t i = 0; i < info_count; i++) {
+ auto& info = GetInfo(i);
+ auto& in_params = info.GetInParams();
+ // Populate our sorted info
+ sorted_info[i] = &info;
+
+ if (!in_params.in_use) {
+ continue;
+ }
+
+ auto mix_id = in_params.mix_id;
+ // Needs to be referenced out of scope
+ s32 distance_to_final_mix{AudioCommon::FINAL_MIX};
+ for (; distance_to_final_mix < static_cast<s32>(info_count); distance_to_final_mix++) {
+ if (mix_id == AudioCommon::FINAL_MIX) {
+ // If we're at the final mix, we're done
+ break;
+ } else if (mix_id == AudioCommon::NO_MIX) {
+ // If we have no more mix ids, we're done
+ distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
+ break;
+ } else {
+ const auto& dest_mix = GetInfo(mix_id);
+ const auto dest_mix_distance = dest_mix.GetInParams().final_mix_distance;
+
+ if (dest_mix_distance == AudioCommon::NO_FINAL_MIX) {
+ // If our current mix isn't pointing to a final mix, follow through
+ mix_id = dest_mix.GetInParams().dest_mix_id;
+ } else {
+ // Our current mix + 1 = final distance
+ distance_to_final_mix = dest_mix_distance + 1;
+ break;
+ }
+ }
+ }
+
+ // If we're out of range for our distance, mark it as no final mix
+ if (distance_to_final_mix >= static_cast<s32>(info_count)) {
+ distance_to_final_mix = AudioCommon::NO_FINAL_MIX;
+ }
+
+ in_params.final_mix_distance = distance_to_final_mix;
+ }
+}
+
+void MixContext::CalcMixBufferOffset() {
+ s32 offset{};
+ for (std::size_t i = 0; i < info_count; i++) {
+ auto& info = GetSortedInfo(i);
+ auto& in_params = info.GetInParams();
+ if (in_params.in_use) {
+ // Only update if in use
+ in_params.buffer_offset = offset;
+ offset += in_params.buffer_count;
+ }
+ }
+}
+
+void MixContext::SortInfo() {
+ // Get the distance to the final mix
+ UpdateDistancesFromFinalMix();
+
+ // Sort based on the distance to the final mix
+ std::sort(sorted_info.begin(), sorted_info.end(),
+ [](const ServerMixInfo* lhs, const ServerMixInfo* rhs) {
+ return lhs->GetInParams().final_mix_distance >
+ rhs->GetInParams().final_mix_distance;
+ });
+
+ // Calculate the mix buffer offset
+ CalcMixBufferOffset();
+}
+
+bool MixContext::TsortInfo(SplitterContext& splitter_context) {
+ // If we're not using mixes, just calculate the mix buffer offset
+ if (!splitter_context.UsingSplitter()) {
+ CalcMixBufferOffset();
+ return true;
+ }
+ // Sort our node states
+ if (!node_states.Tsort(edge_matrix)) {
+ return false;
+ }
+
+ // Get our sorted list
+ const auto sorted_list = node_states.GetIndexList();
+ std::size_t info_id{};
+ for (auto itr = sorted_list.rbegin(); itr != sorted_list.rend(); ++itr) {
+ // Set our sorted info
+ sorted_info[info_id++] = &GetInfo(*itr);
+ }
+
+ // Calculate the mix buffer offset
+ CalcMixBufferOffset();
+ return true;
+}
+
+std::size_t MixContext::GetCount() const {
+ return info_count;
+}
+
+ServerMixInfo& MixContext::GetInfo(std::size_t i) {
+ ASSERT(i < info_count);
+ return infos.at(i);
+}
+
+const ServerMixInfo& MixContext::GetInfo(std::size_t i) const {
+ ASSERT(i < info_count);
+ return infos.at(i);
+}
+
+ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) {
+ ASSERT(i < info_count);
+ return *sorted_info.at(i);
+}
+
+const ServerMixInfo& MixContext::GetSortedInfo(std::size_t i) const {
+ ASSERT(i < info_count);
+ return *sorted_info.at(i);
+}
+
+ServerMixInfo& MixContext::GetFinalMixInfo() {
+ return infos.at(AudioCommon::FINAL_MIX);
+}
+
+const ServerMixInfo& MixContext::GetFinalMixInfo() const {
+ return infos.at(AudioCommon::FINAL_MIX);
+}
+
+EdgeMatrix& MixContext::GetEdgeMatrix() {
+ return edge_matrix;
+}
+
+const EdgeMatrix& MixContext::GetEdgeMatrix() const {
+ return edge_matrix;
+}
+
+ServerMixInfo::ServerMixInfo() {
+ Cleanup();
+}
+ServerMixInfo::~ServerMixInfo() = default;
+
+const ServerMixInfo::InParams& ServerMixInfo::GetInParams() const {
+ return in_params;
+}
+
+ServerMixInfo::InParams& ServerMixInfo::GetInParams() {
+ return in_params;
+}
+
+bool ServerMixInfo::Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
+ BehaviorInfo& behavior_info, SplitterContext& splitter_context,
+ EffectContext& effect_context) {
+ in_params.volume = mix_in.volume;
+ in_params.sample_rate = mix_in.sample_rate;
+ in_params.buffer_count = mix_in.buffer_count;
+ in_params.in_use = mix_in.in_use;
+ in_params.mix_id = mix_in.mix_id;
+ in_params.node_id = mix_in.node_id;
+ for (std::size_t i = 0; i < mix_in.mix_volume.size(); i++) {
+ std::copy(mix_in.mix_volume[i].begin(), mix_in.mix_volume[i].end(),
+ in_params.mix_volume[i].begin());
+ }
+
+ bool require_sort = false;
+
+ if (behavior_info.IsSplitterSupported()) {
+ require_sort = UpdateConnection(edge_matrix, mix_in, splitter_context);
+ } else {
+ in_params.dest_mix_id = mix_in.dest_mix_id;
+ in_params.splitter_id = AudioCommon::NO_SPLITTER;
+ }
+
+ ResetEffectProcessingOrder();
+ const auto effect_count = effect_context.GetCount();
+ for (std::size_t i = 0; i < effect_count; i++) {
+ auto* effect_info = effect_context.GetInfo(i);
+ if (effect_info->GetMixID() == in_params.mix_id) {
+ effect_processing_order[effect_info->GetProcessingOrder()] = static_cast<s32>(i);
+ }
+ }
+
+ // TODO(ogniK): Update effect processing order
+ return require_sort;
+}
+
+bool ServerMixInfo::HasAnyConnection() const {
+ return in_params.splitter_id != AudioCommon::NO_SPLITTER ||
+ in_params.mix_id != AudioCommon::NO_MIX;
+}
+
+void ServerMixInfo::Cleanup() {
+ in_params.volume = 0.0f;
+ in_params.sample_rate = 0;
+ in_params.buffer_count = 0;
+ in_params.in_use = false;
+ in_params.mix_id = AudioCommon::NO_MIX;
+ in_params.node_id = 0;
+ in_params.buffer_offset = 0;
+ in_params.dest_mix_id = AudioCommon::NO_MIX;
+ in_params.splitter_id = AudioCommon::NO_SPLITTER;
+ std::memset(in_params.mix_volume.data(), 0, sizeof(float) * in_params.mix_volume.size());
+}
+
+void ServerMixInfo::SetEffectCount(std::size_t count) {
+ effect_processing_order.resize(count);
+ ResetEffectProcessingOrder();
+}
+
+void ServerMixInfo::ResetEffectProcessingOrder() {
+ for (auto& order : effect_processing_order) {
+ order = AudioCommon::NO_EFFECT_ORDER;
+ }
+}
+
+s32 ServerMixInfo::GetEffectOrder(std::size_t i) const {
+ return effect_processing_order.at(i);
+}
+
+bool ServerMixInfo::UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
+ SplitterContext& splitter_context) {
+ // Mixes are identical
+ if (in_params.dest_mix_id == mix_in.dest_mix_id &&
+ in_params.splitter_id == mix_in.splitter_id &&
+ ((in_params.splitter_id == AudioCommon::NO_SPLITTER) ||
+ !splitter_context.GetInfo(in_params.splitter_id).HasNewConnection())) {
+ return false;
+ }
+ // Remove current edges for mix id
+ edge_matrix.RemoveEdges(in_params.mix_id);
+ if (mix_in.dest_mix_id != AudioCommon::NO_MIX) {
+ // If we have a valid destination mix id, set our edge matrix
+ edge_matrix.Connect(in_params.mix_id, mix_in.dest_mix_id);
+ } else if (mix_in.splitter_id != AudioCommon::NO_SPLITTER) {
+ // Recurse our splitter linked and set our edges
+ auto& splitter_info = splitter_context.GetInfo(mix_in.splitter_id);
+ const auto length = splitter_info.GetLength();
+ for (s32 i = 0; i < length; i++) {
+ const auto* splitter_destination =
+ splitter_context.GetDestinationData(mix_in.splitter_id, i);
+ if (splitter_destination == nullptr) {
+ continue;
+ }
+ if (splitter_destination->ValidMixId()) {
+ edge_matrix.Connect(in_params.mix_id, splitter_destination->GetMixId());
+ }
+ }
+ }
+ in_params.dest_mix_id = mix_in.dest_mix_id;
+ in_params.splitter_id = mix_in.splitter_id;
+ return true;
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/mix_context.h b/src/audio_core/mix_context.h
new file mode 100644
index 000000000..6a588eeb4
--- /dev/null
+++ b/src/audio_core/mix_context.h
@@ -0,0 +1,114 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+#include "audio_core/common.h"
+#include "audio_core/splitter_context.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace AudioCore {
+class BehaviorInfo;
+class EffectContext;
+
+class MixInfo {
+public:
+ struct DirtyHeader {
+ u32_le magic{};
+ u32_le mixer_count{};
+ INSERT_PADDING_BYTES(0x18);
+ };
+ static_assert(sizeof(DirtyHeader) == 0x20, "MixInfo::DirtyHeader is an invalid size");
+
+ struct InParams {
+ float_le volume{};
+ s32_le sample_rate{};
+ s32_le buffer_count{};
+ bool in_use{};
+ INSERT_PADDING_BYTES(3);
+ s32_le mix_id{};
+ s32_le effect_count{};
+ u32_le node_id{};
+ INSERT_PADDING_WORDS(2);
+ std::array<std::array<float_le, AudioCommon::MAX_MIX_BUFFERS>, AudioCommon::MAX_MIX_BUFFERS>
+ mix_volume{};
+ s32_le dest_mix_id{};
+ s32_le splitter_id{};
+ INSERT_PADDING_WORDS(1);
+ };
+ static_assert(sizeof(MixInfo::InParams) == 0x930, "MixInfo::InParams is an invalid size");
+};
+
+class ServerMixInfo {
+public:
+ struct InParams {
+ float volume{};
+ s32 sample_rate{};
+ s32 buffer_count{};
+ bool in_use{};
+ s32 mix_id{};
+ u32 node_id{};
+ std::array<std::array<float_le, AudioCommon::MAX_MIX_BUFFERS>, AudioCommon::MAX_MIX_BUFFERS>
+ mix_volume{};
+ s32 dest_mix_id{};
+ s32 splitter_id{};
+ s32 buffer_offset{};
+ s32 final_mix_distance{};
+ };
+ ServerMixInfo();
+ ~ServerMixInfo();
+
+ const ServerMixInfo::InParams& GetInParams() const;
+ ServerMixInfo::InParams& GetInParams();
+
+ bool Update(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
+ BehaviorInfo& behavior_info, SplitterContext& splitter_context,
+ EffectContext& effect_context);
+ bool HasAnyConnection() const;
+ void Cleanup();
+ void SetEffectCount(std::size_t count);
+ void ResetEffectProcessingOrder();
+ s32 GetEffectOrder(std::size_t i) const;
+
+private:
+ std::vector<s32> effect_processing_order;
+ InParams in_params{};
+ bool UpdateConnection(EdgeMatrix& edge_matrix, const MixInfo::InParams& mix_in,
+ SplitterContext& splitter_context);
+};
+
+class MixContext {
+public:
+ MixContext();
+ ~MixContext();
+
+ void Initialize(const BehaviorInfo& behavior_info, std::size_t mix_count,
+ std::size_t effect_count);
+ void SortInfo();
+ bool TsortInfo(SplitterContext& splitter_context);
+
+ std::size_t GetCount() const;
+ ServerMixInfo& GetInfo(std::size_t i);
+ const ServerMixInfo& GetInfo(std::size_t i) const;
+ ServerMixInfo& GetSortedInfo(std::size_t i);
+ const ServerMixInfo& GetSortedInfo(std::size_t i) const;
+ ServerMixInfo& GetFinalMixInfo();
+ const ServerMixInfo& GetFinalMixInfo() const;
+ EdgeMatrix& GetEdgeMatrix();
+ const EdgeMatrix& GetEdgeMatrix() const;
+
+private:
+ void CalcMixBufferOffset();
+ void UpdateDistancesFromFinalMix();
+
+ NodeStates node_states{};
+ EdgeMatrix edge_matrix{};
+ std::size_t info_count{};
+ std::vector<ServerMixInfo> infos{};
+ std::vector<ServerMixInfo*> sorted_info{};
+};
+} // namespace AudioCore
diff --git a/src/audio_core/sink_context.cpp b/src/audio_core/sink_context.cpp
new file mode 100644
index 000000000..0882b411a
--- /dev/null
+++ b/src/audio_core/sink_context.cpp
@@ -0,0 +1,31 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/sink_context.h"
+
+namespace AudioCore {
+SinkContext::SinkContext(std::size_t sink_count) : sink_count(sink_count) {}
+SinkContext::~SinkContext() = default;
+
+std::size_t SinkContext::GetCount() const {
+ return sink_count;
+}
+
+void SinkContext::UpdateMainSink(SinkInfo::InParams& in) {
+ in_use = in.in_use;
+ use_count = in.device.input_count;
+ std::memcpy(buffers.data(), in.device.input.data(), AudioCommon::MAX_CHANNEL_COUNT);
+}
+
+bool SinkContext::InUse() const {
+ return in_use;
+}
+
+std::vector<u8> SinkContext::OutputBuffers() const {
+ std::vector<u8> buffer_ret(use_count);
+ std::memcpy(buffer_ret.data(), buffers.data(), use_count);
+ return buffer_ret;
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/sink_context.h b/src/audio_core/sink_context.h
new file mode 100644
index 000000000..d7aa72ba7
--- /dev/null
+++ b/src/audio_core/sink_context.h
@@ -0,0 +1,89 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "audio_core/common.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace AudioCore {
+
+enum class SinkTypes : u8 {
+ Invalid = 0,
+ Device = 1,
+ Circular = 2,
+};
+
+enum class SinkSampleFormat : u32_le {
+ None = 0,
+ Pcm8 = 1,
+ Pcm16 = 2,
+ Pcm24 = 3,
+ Pcm32 = 4,
+ PcmFloat = 5,
+ Adpcm = 6,
+};
+
+class SinkInfo {
+public:
+ struct CircularBufferIn {
+ u64_le address;
+ u32_le size;
+ u32_le input_count;
+ u32_le sample_count;
+ u32_le previous_position;
+ SinkSampleFormat sample_format;
+ std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
+ bool in_use;
+ INSERT_UNION_PADDING_BYTES(5);
+ };
+ static_assert(sizeof(SinkInfo::CircularBufferIn) == 0x28,
+ "SinkInfo::CircularBufferIn is in invalid size");
+
+ struct DeviceIn {
+ std::array<u8, 255> device_name;
+ INSERT_UNION_PADDING_BYTES(1);
+ s32_le input_count;
+ std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> input;
+ INSERT_UNION_PADDING_BYTES(1);
+ bool down_matrix_enabled;
+ std::array<float_le, 4> down_matrix_coef;
+ };
+ static_assert(sizeof(SinkInfo::DeviceIn) == 0x11c, "SinkInfo::DeviceIn is an invalid size");
+
+ struct InParams {
+ SinkTypes type{};
+ bool in_use{};
+ INSERT_PADDING_BYTES(2);
+ u32_le node_id{};
+ INSERT_PADDING_WORDS(6);
+ union {
+ // std::array<u8, 0x120> raw{};
+ SinkInfo::DeviceIn device;
+ SinkInfo::CircularBufferIn circular_buffer;
+ };
+ };
+ static_assert(sizeof(SinkInfo::InParams) == 0x140, "SinkInfo::InParams are an invalid size!");
+};
+
+class SinkContext {
+public:
+ explicit SinkContext(std::size_t sink_count);
+ ~SinkContext();
+
+ std::size_t GetCount() const;
+
+ void UpdateMainSink(SinkInfo::InParams& in);
+ bool InUse() const;
+ std::vector<u8> OutputBuffers() const;
+
+private:
+ bool in_use{false};
+ s32 use_count{};
+ std::array<u8, AudioCommon::MAX_CHANNEL_COUNT> buffers{};
+ std::size_t sink_count{};
+};
+} // namespace AudioCore
diff --git a/src/audio_core/splitter_context.cpp b/src/audio_core/splitter_context.cpp
new file mode 100644
index 000000000..f21b53147
--- /dev/null
+++ b/src/audio_core/splitter_context.cpp
@@ -0,0 +1,617 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/behavior_info.h"
+#include "audio_core/splitter_context.h"
+#include "common/alignment.h"
+#include "common/assert.h"
+#include "common/logging/log.h"
+
+namespace AudioCore {
+
+ServerSplitterDestinationData::ServerSplitterDestinationData(s32 id) : id(id) {}
+ServerSplitterDestinationData::~ServerSplitterDestinationData() = default;
+
+void ServerSplitterDestinationData::Update(SplitterInfo::InDestinationParams& header) {
+ // Log error as these are not actually failure states
+ if (header.magic != SplitterMagic::DataHeader) {
+ LOG_ERROR(Audio, "Splitter destination header is invalid!");
+ return;
+ }
+
+ // Incorrect splitter id
+ if (header.splitter_id != id) {
+ LOG_ERROR(Audio, "Splitter destination ids do not match!");
+ return;
+ }
+
+ mix_id = header.mix_id;
+ // Copy our mix volumes
+ std::copy(header.mix_volumes.begin(), header.mix_volumes.end(), current_mix_volumes.begin());
+ if (!in_use && header.in_use) {
+ // Update mix volumes
+ std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
+ needs_update = false;
+ }
+ in_use = header.in_use;
+}
+
+ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() {
+ return next;
+}
+
+const ServerSplitterDestinationData* ServerSplitterDestinationData::GetNextDestination() const {
+ return next;
+}
+
+void ServerSplitterDestinationData::SetNextDestination(ServerSplitterDestinationData* dest) {
+ next = dest;
+}
+
+bool ServerSplitterDestinationData::ValidMixId() const {
+ return GetMixId() != AudioCommon::NO_MIX;
+}
+
+s32 ServerSplitterDestinationData::GetMixId() const {
+ return mix_id;
+}
+
+bool ServerSplitterDestinationData::IsConfigured() const {
+ return in_use && ValidMixId();
+}
+
+float ServerSplitterDestinationData::GetMixVolume(std::size_t i) const {
+ ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
+ return current_mix_volumes.at(i);
+}
+
+const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
+ServerSplitterDestinationData::CurrentMixVolumes() const {
+ return current_mix_volumes;
+}
+
+const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
+ServerSplitterDestinationData::LastMixVolumes() const {
+ return last_mix_volumes;
+}
+
+void ServerSplitterDestinationData::MarkDirty() {
+ needs_update = true;
+}
+
+void ServerSplitterDestinationData::UpdateInternalState() {
+ if (in_use && needs_update) {
+ std::copy(current_mix_volumes.begin(), current_mix_volumes.end(), last_mix_volumes.begin());
+ }
+ needs_update = false;
+}
+
+ServerSplitterInfo::ServerSplitterInfo(s32 id) : id(id) {}
+ServerSplitterInfo::~ServerSplitterInfo() = default;
+
+void ServerSplitterInfo::InitializeInfos() {
+ send_length = 0;
+ head = nullptr;
+ new_connection = true;
+}
+
+void ServerSplitterInfo::ClearNewConnectionFlag() {
+ new_connection = false;
+}
+
+std::size_t ServerSplitterInfo::Update(SplitterInfo::InInfoPrams& header) {
+ if (header.send_id != id) {
+ return 0;
+ }
+
+ sample_rate = header.sample_rate;
+ new_connection = true;
+ // We need to update the size here due to the splitter bug being present and providing an
+ // incorrect size. We're suppose to also update the header here but we just ignore and continue
+ return (sizeof(s32_le) * (header.length - 1)) + (sizeof(s32_le) * 3);
+}
+
+ServerSplitterDestinationData* ServerSplitterInfo::GetHead() {
+ return head;
+}
+
+const ServerSplitterDestinationData* ServerSplitterInfo::GetHead() const {
+ return head;
+}
+
+ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) {
+ auto current_head = head;
+ for (std::size_t i = 0; i < depth; i++) {
+ if (current_head == nullptr) {
+ return nullptr;
+ }
+ current_head = current_head->GetNextDestination();
+ }
+ return current_head;
+}
+
+const ServerSplitterDestinationData* ServerSplitterInfo::GetData(std::size_t depth) const {
+ auto current_head = head;
+ for (std::size_t i = 0; i < depth; i++) {
+ if (current_head == nullptr) {
+ return nullptr;
+ }
+ current_head = current_head->GetNextDestination();
+ }
+ return current_head;
+}
+
+bool ServerSplitterInfo::HasNewConnection() const {
+ return new_connection;
+}
+
+s32 ServerSplitterInfo::GetLength() const {
+ return send_length;
+}
+
+void ServerSplitterInfo::SetHead(ServerSplitterDestinationData* new_head) {
+ head = new_head;
+}
+
+void ServerSplitterInfo::SetHeadDepth(s32 length) {
+ send_length = length;
+}
+
+SplitterContext::SplitterContext() = default;
+SplitterContext::~SplitterContext() = default;
+
+void SplitterContext::Initialize(BehaviorInfo& behavior_info, std::size_t _info_count,
+ std::size_t _data_count) {
+ if (!behavior_info.IsSplitterSupported() || _data_count == 0 || _info_count == 0) {
+ Setup(0, 0, false);
+ return;
+ }
+ // Only initialize if we're using splitters
+ Setup(_info_count, _data_count, behavior_info.IsSplitterBugFixed());
+}
+
+bool SplitterContext::Update(const std::vector<u8>& input, std::size_t& input_offset,
+ std::size_t& bytes_read) {
+ const auto UpdateOffsets = [&](std::size_t read) {
+ input_offset += read;
+ bytes_read += read;
+ };
+
+ if (info_count == 0 || data_count == 0) {
+ bytes_read = 0;
+ return true;
+ }
+
+ if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
+ sizeof(SplitterInfo::InHeader))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+ SplitterInfo::InHeader header{};
+ std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InHeader));
+ UpdateOffsets(sizeof(SplitterInfo::InHeader));
+
+ if (header.magic != SplitterMagic::SplitterHeader) {
+ LOG_ERROR(Audio, "Invalid header magic! Expecting {:X} but got {:X}",
+ SplitterMagic::SplitterHeader, header.magic);
+ return false;
+ }
+
+ // Clear all connections
+ for (auto& info : infos) {
+ info.ClearNewConnectionFlag();
+ }
+
+ UpdateInfo(input, input_offset, bytes_read, header.info_count);
+ UpdateData(input, input_offset, bytes_read, header.data_count);
+ const auto aligned_bytes_read = Common::AlignUp(bytes_read, 16);
+ input_offset += aligned_bytes_read - bytes_read;
+ bytes_read = aligned_bytes_read;
+ return true;
+}
+
+bool SplitterContext::UsingSplitter() const {
+ return info_count > 0 && data_count > 0;
+}
+
+ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) {
+ ASSERT(i < info_count);
+ return infos.at(i);
+}
+
+const ServerSplitterInfo& SplitterContext::GetInfo(std::size_t i) const {
+ ASSERT(i < info_count);
+ return infos.at(i);
+}
+
+ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) {
+ ASSERT(i < data_count);
+ return datas.at(i);
+}
+
+const ServerSplitterDestinationData& SplitterContext::GetData(std::size_t i) const {
+ ASSERT(i < data_count);
+ return datas.at(i);
+}
+
+ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
+ std::size_t data) {
+ ASSERT(info < info_count);
+ auto& cur_info = GetInfo(info);
+ return cur_info.GetData(data);
+}
+
+const ServerSplitterDestinationData* SplitterContext::GetDestinationData(std::size_t info,
+ std::size_t data) const {
+ ASSERT(info < info_count);
+ auto& cur_info = GetInfo(info);
+ return cur_info.GetData(data);
+}
+
+void SplitterContext::UpdateInternalState() {
+ if (data_count == 0) {
+ return;
+ }
+
+ for (auto& data : datas) {
+ data.UpdateInternalState();
+ }
+}
+
+std::size_t SplitterContext::GetInfoCount() const {
+ return info_count;
+}
+
+std::size_t SplitterContext::GetDataCount() const {
+ return data_count;
+}
+
+void SplitterContext::Setup(std::size_t _info_count, std::size_t _data_count,
+ bool is_splitter_bug_fixed) {
+
+ info_count = _info_count;
+ data_count = _data_count;
+
+ for (std::size_t i = 0; i < info_count; i++) {
+ auto& splitter = infos.emplace_back(static_cast<s32>(i));
+ splitter.InitializeInfos();
+ }
+ for (std::size_t i = 0; i < data_count; i++) {
+ datas.emplace_back(static_cast<s32>(i));
+ }
+
+ bug_fixed = is_splitter_bug_fixed;
+}
+
+bool SplitterContext::UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
+ std::size_t& bytes_read, s32 in_splitter_count) {
+ const auto UpdateOffsets = [&](std::size_t read) {
+ input_offset += read;
+ bytes_read += read;
+ };
+
+ for (s32 i = 0; i < in_splitter_count; i++) {
+ if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
+ sizeof(SplitterInfo::InInfoPrams))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+ SplitterInfo::InInfoPrams header{};
+ std::memcpy(&header, input.data() + input_offset, sizeof(SplitterInfo::InInfoPrams));
+
+ // Logged as warning as these don't actually cause a bailout for some reason
+ if (header.magic != SplitterMagic::InfoHeader) {
+ LOG_ERROR(Audio, "Bad splitter data header");
+ break;
+ }
+
+ if (header.send_id < 0 || static_cast<std::size_t>(header.send_id) > info_count) {
+ LOG_ERROR(Audio, "Bad splitter data id");
+ break;
+ }
+
+ UpdateOffsets(sizeof(SplitterInfo::InInfoPrams));
+ auto& info = GetInfo(header.send_id);
+ if (!RecomposeDestination(info, header, input, input_offset)) {
+ LOG_ERROR(Audio, "Failed to recompose destination for splitter!");
+ return false;
+ }
+ const std::size_t read = info.Update(header);
+ bytes_read += read;
+ input_offset += read;
+ }
+ return true;
+}
+
+bool SplitterContext::UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
+ std::size_t& bytes_read, s32 in_data_count) {
+ const auto UpdateOffsets = [&](std::size_t read) {
+ input_offset += read;
+ bytes_read += read;
+ };
+
+ for (s32 i = 0; i < in_data_count; i++) {
+ if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
+ sizeof(SplitterInfo::InDestinationParams))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+ SplitterInfo::InDestinationParams header{};
+ std::memcpy(&header, input.data() + input_offset,
+ sizeof(SplitterInfo::InDestinationParams));
+ UpdateOffsets(sizeof(SplitterInfo::InDestinationParams));
+
+ // Logged as warning as these don't actually cause a bailout for some reason
+ if (header.magic != SplitterMagic::DataHeader) {
+ LOG_ERROR(Audio, "Bad splitter data header");
+ break;
+ }
+
+ if (header.splitter_id < 0 || static_cast<std::size_t>(header.splitter_id) > data_count) {
+ LOG_ERROR(Audio, "Bad splitter data id");
+ break;
+ }
+ GetData(header.splitter_id).Update(header);
+ }
+ return true;
+}
+
+bool SplitterContext::RecomposeDestination(ServerSplitterInfo& info,
+ SplitterInfo::InInfoPrams& header,
+ const std::vector<u8>& input,
+ const std::size_t& input_offset) {
+ // Clear our current destinations
+ auto* current_head = info.GetHead();
+ while (current_head != nullptr) {
+ auto next_head = current_head->GetNextDestination();
+ current_head->SetNextDestination(nullptr);
+ current_head = next_head;
+ }
+ info.SetHead(nullptr);
+
+ s32 size = header.length;
+ // If the splitter bug is present, calculate fixed size
+ if (!bug_fixed) {
+ if (info_count > 0) {
+ const auto factor = data_count / info_count;
+ size = std::min(header.length, static_cast<s32>(factor));
+ } else {
+ size = 0;
+ }
+ }
+
+ if (size < 1) {
+ LOG_ERROR(Audio, "Invalid splitter info size! size={:X}", size);
+ return true;
+ }
+
+ auto* start_head = &GetData(header.resource_id_base);
+ current_head = start_head;
+ std::vector<s32_le> resource_ids(size - 1);
+ if (!AudioCommon::CanConsumeBuffer(input.size(), input_offset,
+ resource_ids.size() * sizeof(s32_le))) {
+ LOG_ERROR(Audio, "Buffer is an invalid size!");
+ return false;
+ }
+ std::memcpy(resource_ids.data(), input.data() + input_offset,
+ resource_ids.size() * sizeof(s32_le));
+
+ for (auto resource_id : resource_ids) {
+ auto* head = &GetData(resource_id);
+ current_head->SetNextDestination(head);
+ current_head = head;
+ }
+
+ info.SetHead(start_head);
+ info.SetHeadDepth(size);
+
+ return true;
+}
+
+NodeStates::NodeStates() = default;
+NodeStates::~NodeStates() = default;
+
+void NodeStates::Initialize(std::size_t node_count_) {
+ // Setup our work parameters
+ node_count = node_count_;
+ was_node_found.resize(node_count);
+ was_node_completed.resize(node_count);
+ index_list.resize(node_count);
+ index_stack.Reset(node_count * node_count);
+}
+
+bool NodeStates::Tsort(EdgeMatrix& edge_matrix) {
+ return DepthFirstSearch(edge_matrix);
+}
+
+std::size_t NodeStates::GetIndexPos() const {
+ return index_pos;
+}
+
+const std::vector<s32>& NodeStates::GetIndexList() const {
+ return index_list;
+}
+
+void NodeStates::PushTsortResult(s32 index) {
+ ASSERT(index < static_cast<s32>(node_count));
+ index_list[index_pos++] = index;
+}
+
+bool NodeStates::DepthFirstSearch(EdgeMatrix& edge_matrix) {
+ ResetState();
+ for (std::size_t i = 0; i < node_count; i++) {
+ const auto node_id = static_cast<s32>(i);
+
+ // If we don't have a state, send to our index stack for work
+ if (GetState(i) == NodeStates::State::NoState) {
+ index_stack.push(node_id);
+ }
+
+ // While we have work to do in our stack
+ while (index_stack.Count() > 0) {
+ // Get the current node
+ const auto current_stack_index = index_stack.top();
+ // Check if we've seen the node yet
+ const auto index_state = GetState(current_stack_index);
+ if (index_state == NodeStates::State::NoState) {
+ // Mark the node as seen
+ UpdateState(NodeStates::State::InFound, current_stack_index);
+ } else if (index_state == NodeStates::State::InFound) {
+ // We've seen this node before, mark it as completed
+ UpdateState(NodeStates::State::InCompleted, current_stack_index);
+ // Update our index list
+ PushTsortResult(current_stack_index);
+ // Pop the stack
+ index_stack.pop();
+ continue;
+ } else if (index_state == NodeStates::State::InCompleted) {
+ // If our node is already sorted, clear it
+ index_stack.pop();
+ continue;
+ }
+
+ const auto node_count = edge_matrix.GetNodeCount();
+ for (s32 j = 0; j < static_cast<s32>(node_count); j++) {
+ // Check if our node is connected to our edge matrix
+ if (!edge_matrix.Connected(current_stack_index, j)) {
+ continue;
+ }
+
+ // Check if our node exists
+ const auto node_state = GetState(j);
+ if (node_state == NodeStates::State::NoState) {
+ // Add more work
+ index_stack.push(j);
+ } else if (node_state == NodeStates::State::InFound) {
+ UNREACHABLE_MSG("Node start marked as found");
+ ResetState();
+ return false;
+ }
+ }
+ }
+ }
+ return true;
+}
+
+void NodeStates::ResetState() {
+ // Reset to the start of our index stack
+ index_pos = 0;
+ for (std::size_t i = 0; i < node_count; i++) {
+ // Mark all nodes as not found
+ was_node_found[i] = false;
+ // Mark all nodes as uncompleted
+ was_node_completed[i] = false;
+ // Mark all indexes as invalid
+ index_list[i] = -1;
+ }
+}
+
+void NodeStates::UpdateState(NodeStates::State state, std::size_t i) {
+ switch (state) {
+ case NodeStates::State::NoState:
+ was_node_found[i] = false;
+ was_node_completed[i] = false;
+ break;
+ case NodeStates::State::InFound:
+ was_node_found[i] = true;
+ was_node_completed[i] = false;
+ break;
+ case NodeStates::State::InCompleted:
+ was_node_found[i] = false;
+ was_node_completed[i] = true;
+ break;
+ }
+}
+
+NodeStates::State NodeStates::GetState(std::size_t i) {
+ ASSERT(i < node_count);
+ if (was_node_found[i]) {
+ // If our node exists in our found list
+ return NodeStates::State::InFound;
+ } else if (was_node_completed[i]) {
+ // If node is in the completed list
+ return NodeStates::State::InCompleted;
+ } else {
+ // If in neither
+ return NodeStates::State::NoState;
+ }
+}
+
+NodeStates::Stack::Stack() = default;
+NodeStates::Stack::~Stack() = default;
+
+void NodeStates::Stack::Reset(std::size_t size) {
+ // Mark our stack as empty
+ stack.resize(size);
+ stack_size = size;
+ stack_pos = 0;
+ std::fill(stack.begin(), stack.end(), 0);
+}
+
+void NodeStates::Stack::push(s32 val) {
+ ASSERT(stack_pos < stack_size);
+ stack[stack_pos++] = val;
+}
+
+std::size_t NodeStates::Stack::Count() const {
+ return stack_pos;
+}
+
+s32 NodeStates::Stack::top() const {
+ ASSERT(stack_pos > 0);
+ return stack[stack_pos - 1];
+}
+
+s32 NodeStates::Stack::pop() {
+ ASSERT(stack_pos > 0);
+ stack_pos--;
+ return stack[stack_pos];
+}
+
+EdgeMatrix::EdgeMatrix() = default;
+EdgeMatrix::~EdgeMatrix() = default;
+
+void EdgeMatrix::Initialize(std::size_t _node_count) {
+ node_count = _node_count;
+ edge_matrix.resize(node_count * node_count);
+}
+
+bool EdgeMatrix::Connected(s32 a, s32 b) {
+ return GetState(a, b);
+}
+
+void EdgeMatrix::Connect(s32 a, s32 b) {
+ SetState(a, b, true);
+}
+
+void EdgeMatrix::Disconnect(s32 a, s32 b) {
+ SetState(a, b, false);
+}
+
+void EdgeMatrix::RemoveEdges(s32 edge) {
+ for (std::size_t i = 0; i < node_count; i++) {
+ SetState(edge, static_cast<s32>(i), false);
+ }
+}
+
+std::size_t EdgeMatrix::GetNodeCount() const {
+ return node_count;
+}
+
+void EdgeMatrix::SetState(s32 a, s32 b, bool state) {
+ ASSERT(InRange(a, b));
+ edge_matrix.at(a * node_count + b) = state;
+}
+
+bool EdgeMatrix::GetState(s32 a, s32 b) {
+ ASSERT(InRange(a, b));
+ return edge_matrix.at(a * node_count + b);
+}
+
+bool EdgeMatrix::InRange(s32 a, s32 b) const {
+ const std::size_t pos = a * node_count + b;
+ return pos < (node_count * node_count);
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/splitter_context.h b/src/audio_core/splitter_context.h
new file mode 100644
index 000000000..ea6239fdb
--- /dev/null
+++ b/src/audio_core/splitter_context.h
@@ -0,0 +1,221 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <stack>
+#include <vector>
+#include "audio_core/common.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "common/swap.h"
+
+namespace AudioCore {
+class BehaviorInfo;
+
+class EdgeMatrix {
+public:
+ EdgeMatrix();
+ ~EdgeMatrix();
+
+ void Initialize(std::size_t _node_count);
+ bool Connected(s32 a, s32 b);
+ void Connect(s32 a, s32 b);
+ void Disconnect(s32 a, s32 b);
+ void RemoveEdges(s32 edge);
+ std::size_t GetNodeCount() const;
+
+private:
+ void SetState(s32 a, s32 b, bool state);
+ bool GetState(s32 a, s32 b);
+
+ bool InRange(s32 a, s32 b) const;
+ std::vector<bool> edge_matrix{};
+ std::size_t node_count{};
+};
+
+class NodeStates {
+public:
+ enum class State {
+ NoState = 0,
+ InFound = 1,
+ InCompleted = 2,
+ };
+
+ // Looks to be a fixed size stack. Placed within the NodeStates class based on symbols
+ class Stack {
+ public:
+ Stack();
+ ~Stack();
+
+ void Reset(std::size_t size);
+ void push(s32 val);
+ std::size_t Count() const;
+ s32 top() const;
+ s32 pop();
+
+ private:
+ std::vector<s32> stack{};
+ std::size_t stack_size{};
+ std::size_t stack_pos{};
+ };
+ NodeStates();
+ ~NodeStates();
+
+ void Initialize(std::size_t _node_count);
+ bool Tsort(EdgeMatrix& edge_matrix);
+ std::size_t GetIndexPos() const;
+ const std::vector<s32>& GetIndexList() const;
+
+private:
+ void PushTsortResult(s32 index);
+ bool DepthFirstSearch(EdgeMatrix& edge_matrix);
+ void ResetState();
+ void UpdateState(NodeStates::State state, std::size_t i);
+ NodeStates::State GetState(std::size_t i);
+
+ std::size_t node_count{};
+ std::vector<bool> was_node_found{};
+ std::vector<bool> was_node_completed{};
+ std::size_t index_pos{};
+ std::vector<s32> index_list{};
+ NodeStates::Stack index_stack{};
+};
+
+enum class SplitterMagic : u32_le {
+ SplitterHeader = Common::MakeMagic('S', 'N', 'D', 'H'),
+ DataHeader = Common::MakeMagic('S', 'N', 'D', 'D'),
+ InfoHeader = Common::MakeMagic('S', 'N', 'D', 'I'),
+};
+
+class SplitterInfo {
+public:
+ struct InHeader {
+ SplitterMagic magic{};
+ s32_le info_count{};
+ s32_le data_count{};
+ INSERT_PADDING_WORDS(5);
+ };
+ static_assert(sizeof(SplitterInfo::InHeader) == 0x20,
+ "SplitterInfo::InHeader is an invalid size");
+
+ struct InInfoPrams {
+ SplitterMagic magic{};
+ s32_le send_id{};
+ s32_le sample_rate{};
+ s32_le length{};
+ s32_le resource_id_base{};
+ };
+ static_assert(sizeof(SplitterInfo::InInfoPrams) == 0x14,
+ "SplitterInfo::InInfoPrams is an invalid size");
+
+ struct InDestinationParams {
+ SplitterMagic magic{};
+ s32_le splitter_id{};
+ std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> mix_volumes{};
+ s32_le mix_id{};
+ bool in_use{};
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(SplitterInfo::InDestinationParams) == 0x70,
+ "SplitterInfo::InDestinationParams is an invalid size");
+};
+
+class ServerSplitterDestinationData {
+public:
+ explicit ServerSplitterDestinationData(s32 id);
+ ~ServerSplitterDestinationData();
+
+ void Update(SplitterInfo::InDestinationParams& header);
+
+ ServerSplitterDestinationData* GetNextDestination();
+ const ServerSplitterDestinationData* GetNextDestination() const;
+ void SetNextDestination(ServerSplitterDestinationData* dest);
+ bool ValidMixId() const;
+ s32 GetMixId() const;
+ bool IsConfigured() const;
+ float GetMixVolume(std::size_t i) const;
+ const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& CurrentMixVolumes() const;
+ const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& LastMixVolumes() const;
+ void MarkDirty();
+ void UpdateInternalState();
+
+private:
+ bool needs_update{};
+ bool in_use{};
+ s32 id{};
+ s32 mix_id{};
+ std::array<float, AudioCommon::MAX_MIX_BUFFERS> current_mix_volumes{};
+ std::array<float, AudioCommon::MAX_MIX_BUFFERS> last_mix_volumes{};
+ ServerSplitterDestinationData* next = nullptr;
+};
+
+class ServerSplitterInfo {
+public:
+ explicit ServerSplitterInfo(s32 id);
+ ~ServerSplitterInfo();
+
+ void InitializeInfos();
+ void ClearNewConnectionFlag();
+ std::size_t Update(SplitterInfo::InInfoPrams& header);
+
+ ServerSplitterDestinationData* GetHead();
+ const ServerSplitterDestinationData* GetHead() const;
+ ServerSplitterDestinationData* GetData(std::size_t depth);
+ const ServerSplitterDestinationData* GetData(std::size_t depth) const;
+
+ bool HasNewConnection() const;
+ s32 GetLength() const;
+
+ void SetHead(ServerSplitterDestinationData* new_head);
+ void SetHeadDepth(s32 length);
+
+private:
+ s32 sample_rate{};
+ s32 id{};
+ s32 send_length{};
+ ServerSplitterDestinationData* head = nullptr;
+ bool new_connection{};
+};
+
+class SplitterContext {
+public:
+ SplitterContext();
+ ~SplitterContext();
+
+ void Initialize(BehaviorInfo& behavior_info, std::size_t splitter_count,
+ std::size_t data_count);
+
+ bool Update(const std::vector<u8>& input, std::size_t& input_offset, std::size_t& bytes_read);
+ bool UsingSplitter() const;
+
+ ServerSplitterInfo& GetInfo(std::size_t i);
+ const ServerSplitterInfo& GetInfo(std::size_t i) const;
+ ServerSplitterDestinationData& GetData(std::size_t i);
+ const ServerSplitterDestinationData& GetData(std::size_t i) const;
+ ServerSplitterDestinationData* GetDestinationData(std::size_t info, std::size_t data);
+ const ServerSplitterDestinationData* GetDestinationData(std::size_t info,
+ std::size_t data) const;
+ void UpdateInternalState();
+
+ std::size_t GetInfoCount() const;
+ std::size_t GetDataCount() const;
+
+private:
+ void Setup(std::size_t info_count, std::size_t data_count, bool is_splitter_bug_fixed);
+ bool UpdateInfo(const std::vector<u8>& input, std::size_t& input_offset,
+ std::size_t& bytes_read, s32 in_splitter_count);
+ bool UpdateData(const std::vector<u8>& input, std::size_t& input_offset,
+ std::size_t& bytes_read, s32 in_data_count);
+ bool RecomposeDestination(ServerSplitterInfo& info, SplitterInfo::InInfoPrams& header,
+ const std::vector<u8>& input, const std::size_t& input_offset);
+
+ std::vector<ServerSplitterInfo> infos{};
+ std::vector<ServerSplitterDestinationData> datas{};
+
+ std::size_t info_count{};
+ std::size_t data_count{};
+ bool bug_fixed{};
+};
+} // namespace AudioCore
diff --git a/src/audio_core/stream.cpp b/src/audio_core/stream.cpp
index 7be5d5087..4bbb1e0c4 100644
--- a/src/audio_core/stream.cpp
+++ b/src/audio_core/stream.cpp
@@ -12,7 +12,6 @@
#include "common/assert.h"
#include "common/logging/log.h"
#include "core/core_timing.h"
-#include "core/core_timing_util.h"
#include "core/settings.h"
namespace AudioCore {
@@ -104,11 +103,7 @@ void Stream::PlayNextBuffer(std::chrono::nanoseconds ns_late) {
sink_stream.EnqueueSamples(GetNumChannels(), active_buffer->GetSamples());
- const auto time_stretch_delta = Settings::values.enable_audio_stretching.GetValue()
- ? std::chrono::nanoseconds::zero()
- : ns_late;
- const auto future_time = GetBufferReleaseNS(*active_buffer) - time_stretch_delta;
- core_timing.ScheduleEvent(future_time, release_event, {});
+ core_timing.ScheduleEvent(GetBufferReleaseNS(*active_buffer) - ns_late, release_event, {});
}
void Stream::ReleaseActiveBuffer(std::chrono::nanoseconds ns_late) {
diff --git a/src/audio_core/voice_context.cpp b/src/audio_core/voice_context.cpp
new file mode 100644
index 000000000..c46ee55f1
--- /dev/null
+++ b/src/audio_core/voice_context.cpp
@@ -0,0 +1,529 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "audio_core/behavior_info.h"
+#include "audio_core/voice_context.h"
+#include "core/memory.h"
+
+namespace AudioCore {
+
+ServerVoiceChannelResource::ServerVoiceChannelResource(s32 id) : id(id) {}
+ServerVoiceChannelResource::~ServerVoiceChannelResource() = default;
+
+bool ServerVoiceChannelResource::InUse() const {
+ return in_use;
+}
+
+float ServerVoiceChannelResource::GetCurrentMixVolumeAt(std::size_t i) const {
+ ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
+ return mix_volume.at(i);
+}
+
+float ServerVoiceChannelResource::GetLastMixVolumeAt(std::size_t i) const {
+ ASSERT(i < AudioCommon::MAX_MIX_BUFFERS);
+ return last_mix_volume.at(i);
+}
+
+void ServerVoiceChannelResource::Update(VoiceChannelResource::InParams& in_params) {
+ in_use = in_params.in_use;
+ // Update our mix volumes only if it's in use
+ if (in_params.in_use) {
+ mix_volume = in_params.mix_volume;
+ }
+}
+
+void ServerVoiceChannelResource::UpdateLastMixVolumes() {
+ last_mix_volume = mix_volume;
+}
+
+const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
+ServerVoiceChannelResource::GetCurrentMixVolume() const {
+ return mix_volume;
+}
+
+const std::array<float, AudioCommon::MAX_MIX_BUFFERS>&
+ServerVoiceChannelResource::GetLastMixVolume() const {
+ return last_mix_volume;
+}
+
+ServerVoiceInfo::ServerVoiceInfo() {
+ Initialize();
+}
+ServerVoiceInfo::~ServerVoiceInfo() = default;
+
+void ServerVoiceInfo::Initialize() {
+ in_params.in_use = false;
+ in_params.node_id = 0;
+ in_params.id = 0;
+ in_params.current_playstate = ServerPlayState::Stop;
+ in_params.priority = 255;
+ in_params.sample_rate = 0;
+ in_params.sample_format = SampleFormat::Invalid;
+ in_params.channel_count = 0;
+ in_params.pitch = 0.0f;
+ in_params.volume = 0.0f;
+ in_params.last_volume = 0.0f;
+ in_params.biquad_filter.fill({});
+ in_params.wave_buffer_count = 0;
+ in_params.wave_bufffer_head = 0;
+ in_params.mix_id = AudioCommon::NO_MIX;
+ in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
+ in_params.additional_params_address = 0;
+ in_params.additional_params_size = 0;
+ in_params.is_new = false;
+ out_params.played_sample_count = 0;
+ out_params.wave_buffer_consumed = 0;
+ in_params.voice_drop_flag = false;
+ in_params.buffer_mapped = false;
+ in_params.wave_buffer_flush_request_count = 0;
+ in_params.was_biquad_filter_enabled.fill(false);
+
+ for (auto& wave_buffer : in_params.wave_buffer) {
+ wave_buffer.start_sample_offset = 0;
+ wave_buffer.end_sample_offset = 0;
+ wave_buffer.is_looping = false;
+ wave_buffer.end_of_stream = false;
+ wave_buffer.buffer_address = 0;
+ wave_buffer.buffer_size = 0;
+ wave_buffer.context_address = 0;
+ wave_buffer.context_size = 0;
+ wave_buffer.sent_to_dsp = true;
+ }
+
+ stored_samples.clear();
+}
+
+void ServerVoiceInfo::UpdateParameters(const VoiceInfo::InParams& voice_in,
+ BehaviorInfo& behavior_info) {
+ in_params.in_use = voice_in.is_in_use;
+ in_params.id = voice_in.id;
+ in_params.node_id = voice_in.node_id;
+ in_params.last_playstate = in_params.current_playstate;
+ switch (voice_in.play_state) {
+ case PlayState::Paused:
+ in_params.current_playstate = ServerPlayState::Paused;
+ break;
+ case PlayState::Stopped:
+ if (in_params.current_playstate != ServerPlayState::Stop) {
+ in_params.current_playstate = ServerPlayState::RequestStop;
+ }
+ break;
+ case PlayState::Started:
+ in_params.current_playstate = ServerPlayState::Play;
+ break;
+ default:
+ UNREACHABLE_MSG("Unknown playstate {}", voice_in.play_state);
+ break;
+ }
+
+ in_params.priority = voice_in.priority;
+ in_params.sorting_order = voice_in.sorting_order;
+ in_params.sample_rate = voice_in.sample_rate;
+ in_params.sample_format = voice_in.sample_format;
+ in_params.channel_count = voice_in.channel_count;
+ in_params.pitch = voice_in.pitch;
+ in_params.volume = voice_in.volume;
+ in_params.biquad_filter = voice_in.biquad_filter;
+ in_params.wave_buffer_count = voice_in.wave_buffer_count;
+ in_params.wave_bufffer_head = voice_in.wave_buffer_head;
+ if (behavior_info.IsFlushVoiceWaveBuffersSupported()) {
+ const auto in_request_count = in_params.wave_buffer_flush_request_count;
+ const auto voice_request_count = voice_in.wave_buffer_flush_request_count;
+ in_params.wave_buffer_flush_request_count =
+ static_cast<u8>(in_request_count + voice_request_count);
+ }
+ in_params.mix_id = voice_in.mix_id;
+ if (behavior_info.IsSplitterSupported()) {
+ in_params.splitter_info_id = voice_in.splitter_info_id;
+ } else {
+ in_params.splitter_info_id = AudioCommon::NO_SPLITTER;
+ }
+
+ std::memcpy(in_params.voice_channel_resource_id.data(),
+ voice_in.voice_channel_resource_ids.data(),
+ sizeof(s32) * in_params.voice_channel_resource_id.size());
+
+ if (behavior_info.IsVoicePlayedSampleCountResetAtLoopPointSupported()) {
+ in_params.behavior_flags.is_played_samples_reset_at_loop_point =
+ voice_in.behavior_flags.is_played_samples_reset_at_loop_point;
+ } else {
+ in_params.behavior_flags.is_played_samples_reset_at_loop_point.Assign(0);
+ }
+ if (behavior_info.IsVoicePitchAndSrcSkippedSupported()) {
+ in_params.behavior_flags.is_pitch_and_src_skipped =
+ voice_in.behavior_flags.is_pitch_and_src_skipped;
+ } else {
+ in_params.behavior_flags.is_pitch_and_src_skipped.Assign(0);
+ }
+
+ if (voice_in.is_voice_drop_flag_clear_requested) {
+ in_params.voice_drop_flag = false;
+ }
+
+ if (in_params.additional_params_address != voice_in.additional_params_address ||
+ in_params.additional_params_size != voice_in.additional_params_size) {
+ in_params.additional_params_address = voice_in.additional_params_address;
+ in_params.additional_params_size = voice_in.additional_params_size;
+ // TODO(ogniK): Reattach buffer, do we actually need to? Maybe just signal to the DSP that
+ // our context is new
+ }
+}
+
+void ServerVoiceInfo::UpdateWaveBuffers(
+ const VoiceInfo::InParams& voice_in,
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states,
+ BehaviorInfo& behavior_info) {
+ if (voice_in.is_new) {
+ // Initialize our wave buffers
+ for (auto& wave_buffer : in_params.wave_buffer) {
+ wave_buffer.start_sample_offset = 0;
+ wave_buffer.end_sample_offset = 0;
+ wave_buffer.is_looping = false;
+ wave_buffer.end_of_stream = false;
+ wave_buffer.buffer_address = 0;
+ wave_buffer.buffer_size = 0;
+ wave_buffer.context_address = 0;
+ wave_buffer.context_size = 0;
+ wave_buffer.sent_to_dsp = true;
+ }
+
+ // Mark all our wave buffers as invalid
+ for (std::size_t channel = 0; channel < static_cast<std::size_t>(in_params.channel_count);
+ channel++) {
+ for (auto& is_valid : voice_states[channel]->is_wave_buffer_valid) {
+ is_valid = false;
+ }
+ }
+ }
+
+ // Update our wave buffers
+ for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
+ // Assume that we have at least 1 channel voice state
+ const auto have_valid_wave_buffer = voice_states[0]->is_wave_buffer_valid[i];
+
+ UpdateWaveBuffer(in_params.wave_buffer[i], voice_in.wave_buffer[i], in_params.sample_format,
+ have_valid_wave_buffer, behavior_info);
+ }
+}
+
+void ServerVoiceInfo::UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer,
+ const WaveBuffer& in_wave_buffer, SampleFormat sample_format,
+ bool is_buffer_valid, BehaviorInfo& behavior_info) {
+ if (!is_buffer_valid && out_wavebuffer.sent_to_dsp) {
+ out_wavebuffer.buffer_address = 0;
+ out_wavebuffer.buffer_size = 0;
+ }
+
+ if (!in_wave_buffer.sent_to_server || !in_params.buffer_mapped) {
+ // Validate sample offset sizings
+ if (sample_format == SampleFormat::Pcm16) {
+ const auto buffer_size = in_wave_buffer.buffer_size;
+ if (in_wave_buffer.start_sample_offset < 0 || in_wave_buffer.end_sample_offset < 0 ||
+ (buffer_size < (sizeof(s16) * in_wave_buffer.start_sample_offset)) ||
+ (buffer_size < (sizeof(s16) * in_wave_buffer.end_sample_offset))) {
+ // TODO(ogniK): Write error info
+ return;
+ }
+ }
+ // TODO(ogniK): ADPCM Size error
+
+ out_wavebuffer.sent_to_dsp = false;
+ out_wavebuffer.start_sample_offset = in_wave_buffer.start_sample_offset;
+ out_wavebuffer.end_sample_offset = in_wave_buffer.end_sample_offset;
+ out_wavebuffer.is_looping = in_wave_buffer.is_looping;
+ out_wavebuffer.end_of_stream = in_wave_buffer.end_of_stream;
+
+ out_wavebuffer.buffer_address = in_wave_buffer.buffer_address;
+ out_wavebuffer.buffer_size = in_wave_buffer.buffer_size;
+ out_wavebuffer.context_address = in_wave_buffer.context_address;
+ out_wavebuffer.context_size = in_wave_buffer.context_size;
+ in_params.buffer_mapped =
+ in_wave_buffer.buffer_address != 0 && in_wave_buffer.buffer_size != 0;
+ // TODO(ogniK): Pool mapper attachment
+ // TODO(ogniK): IsAdpcmLoopContextBugFixed
+ }
+}
+
+void ServerVoiceInfo::WriteOutStatus(
+ VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states) {
+ if (voice_in.is_new) {
+ in_params.is_new = true;
+ voice_out.wave_buffer_consumed = 0;
+ voice_out.played_sample_count = 0;
+ voice_out.voice_dropped = false;
+ } else if (!in_params.is_new) {
+ voice_out.wave_buffer_consumed = voice_states[0]->wave_buffer_consumed;
+ voice_out.played_sample_count = voice_states[0]->played_sample_count;
+ voice_out.voice_dropped = in_params.voice_drop_flag;
+ } else {
+ voice_out.wave_buffer_consumed = 0;
+ voice_out.played_sample_count = 0;
+ voice_out.voice_dropped = false;
+ }
+}
+
+const ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() const {
+ return in_params;
+}
+
+ServerVoiceInfo::InParams& ServerVoiceInfo::GetInParams() {
+ return in_params;
+}
+
+const ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() const {
+ return out_params;
+}
+
+ServerVoiceInfo::OutParams& ServerVoiceInfo::GetOutParams() {
+ return out_params;
+}
+
+bool ServerVoiceInfo::ShouldSkip() const {
+ // TODO(ogniK): Handle unmapped wave buffers or parameters
+ return !in_params.in_use || (in_params.wave_buffer_count == 0) || in_params.voice_drop_flag;
+}
+
+bool ServerVoiceInfo::UpdateForCommandGeneration(VoiceContext& voice_context) {
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT> dsp_voice_states{};
+ if (in_params.is_new) {
+ ResetResources(voice_context);
+ in_params.last_volume = in_params.volume;
+ in_params.is_new = false;
+ }
+
+ const s32 channel_count = in_params.channel_count;
+ for (s32 i = 0; i < channel_count; i++) {
+ const auto channel_resource = in_params.voice_channel_resource_id[i];
+ dsp_voice_states[i] =
+ &voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
+ }
+ return UpdateParametersForCommandGeneration(dsp_voice_states);
+}
+
+void ServerVoiceInfo::ResetResources(VoiceContext& voice_context) {
+ const s32 channel_count = in_params.channel_count;
+ for (s32 i = 0; i < channel_count; i++) {
+ const auto channel_resource = in_params.voice_channel_resource_id[i];
+ auto& dsp_state =
+ voice_context.GetDspSharedState(static_cast<std::size_t>(channel_resource));
+ dsp_state = {};
+ voice_context.GetChannelResource(static_cast<std::size_t>(channel_resource))
+ .UpdateLastMixVolumes();
+ }
+}
+
+bool ServerVoiceInfo::UpdateParametersForCommandGeneration(
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states) {
+ const s32 channel_count = in_params.channel_count;
+ if (in_params.wave_buffer_flush_request_count > 0) {
+ FlushWaveBuffers(in_params.wave_buffer_flush_request_count, dsp_voice_states,
+ channel_count);
+ in_params.wave_buffer_flush_request_count = 0;
+ }
+
+ switch (in_params.current_playstate) {
+ case ServerPlayState::Play: {
+ for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
+ if (!in_params.wave_buffer[i].sent_to_dsp) {
+ for (s32 channel = 0; channel < channel_count; channel++) {
+ dsp_voice_states[channel]->is_wave_buffer_valid[i] = true;
+ }
+ in_params.wave_buffer[i].sent_to_dsp = true;
+ }
+ }
+ in_params.should_depop = false;
+ return HasValidWaveBuffer(dsp_voice_states[0]);
+ }
+ case ServerPlayState::Paused:
+ case ServerPlayState::Stop: {
+ in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
+ return in_params.should_depop;
+ }
+ case ServerPlayState::RequestStop: {
+ for (std::size_t i = 0; i < AudioCommon::MAX_WAVE_BUFFERS; i++) {
+ in_params.wave_buffer[i].sent_to_dsp = true;
+ for (s32 channel = 0; channel < channel_count; channel++) {
+ auto* dsp_state = dsp_voice_states[channel];
+
+ if (dsp_state->is_wave_buffer_valid[i]) {
+ dsp_state->wave_buffer_index =
+ (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
+ dsp_state->wave_buffer_consumed++;
+ }
+
+ dsp_state->is_wave_buffer_valid[i] = false;
+ }
+ }
+
+ for (s32 channel = 0; channel < channel_count; channel++) {
+ auto* dsp_state = dsp_voice_states[channel];
+ dsp_state->offset = 0;
+ dsp_state->played_sample_count = 0;
+ dsp_state->fraction = 0;
+ dsp_state->sample_history.fill(0);
+ dsp_state->context = {};
+ }
+
+ in_params.current_playstate = ServerPlayState::Stop;
+ in_params.should_depop = in_params.last_playstate == ServerPlayState::Play;
+ return in_params.should_depop;
+ }
+ default:
+ UNREACHABLE_MSG("Invalid playstate {}", in_params.current_playstate);
+ }
+
+ return false;
+}
+
+void ServerVoiceInfo::FlushWaveBuffers(
+ u8 flush_count, std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
+ s32 channel_count) {
+ auto wave_head = in_params.wave_bufffer_head;
+
+ for (u8 i = 0; i < flush_count; i++) {
+ in_params.wave_buffer[wave_head].sent_to_dsp = true;
+ for (s32 channel = 0; channel < channel_count; channel++) {
+ auto* dsp_state = dsp_voice_states[channel];
+ dsp_state->wave_buffer_consumed++;
+ dsp_state->is_wave_buffer_valid[wave_head] = false;
+ dsp_state->wave_buffer_index =
+ (dsp_state->wave_buffer_index + 1) % AudioCommon::MAX_WAVE_BUFFERS;
+ }
+ wave_head = (wave_head + 1) % AudioCommon::MAX_WAVE_BUFFERS;
+ }
+}
+
+bool ServerVoiceInfo::HasValidWaveBuffer(const VoiceState* state) const {
+ const auto& valid_wb = state->is_wave_buffer_valid;
+ return std::find(valid_wb.begin(), valid_wb.end(), true) != valid_wb.end();
+}
+
+VoiceContext::VoiceContext(std::size_t voice_count) : voice_count(voice_count) {
+ for (std::size_t i = 0; i < voice_count; i++) {
+ voice_channel_resources.emplace_back(static_cast<s32>(i));
+ sorted_voice_info.push_back(&voice_info.emplace_back());
+ voice_states.emplace_back();
+ dsp_voice_states.emplace_back();
+ }
+}
+
+VoiceContext::~VoiceContext() {
+ sorted_voice_info.clear();
+}
+
+std::size_t VoiceContext::GetVoiceCount() const {
+ return voice_count;
+}
+
+ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) {
+ ASSERT(i < voice_count);
+ return voice_channel_resources.at(i);
+}
+
+const ServerVoiceChannelResource& VoiceContext::GetChannelResource(std::size_t i) const {
+ ASSERT(i < voice_count);
+ return voice_channel_resources.at(i);
+}
+
+VoiceState& VoiceContext::GetState(std::size_t i) {
+ ASSERT(i < voice_count);
+ return voice_states.at(i);
+}
+
+const VoiceState& VoiceContext::GetState(std::size_t i) const {
+ ASSERT(i < voice_count);
+ return voice_states.at(i);
+}
+
+VoiceState& VoiceContext::GetDspSharedState(std::size_t i) {
+ ASSERT(i < voice_count);
+ return dsp_voice_states.at(i);
+}
+
+const VoiceState& VoiceContext::GetDspSharedState(std::size_t i) const {
+ ASSERT(i < voice_count);
+ return dsp_voice_states.at(i);
+}
+
+ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) {
+ ASSERT(i < voice_count);
+ return voice_info.at(i);
+}
+
+const ServerVoiceInfo& VoiceContext::GetInfo(std::size_t i) const {
+ ASSERT(i < voice_count);
+ return voice_info.at(i);
+}
+
+ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) {
+ ASSERT(i < voice_count);
+ return *sorted_voice_info.at(i);
+}
+
+const ServerVoiceInfo& VoiceContext::GetSortedInfo(std::size_t i) const {
+ ASSERT(i < voice_count);
+ return *sorted_voice_info.at(i);
+}
+
+s32 VoiceContext::DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer, s32 channel,
+ s32 channel_count, s32 buffer_offset, s32 sample_count,
+ Core::Memory::Memory& memory) {
+ if (wave_buffer->buffer_address == 0) {
+ return 0;
+ }
+ if (wave_buffer->buffer_size == 0) {
+ return 0;
+ }
+ if (wave_buffer->end_sample_offset < wave_buffer->start_sample_offset) {
+ return 0;
+ }
+
+ const auto samples_remaining =
+ (wave_buffer->end_sample_offset - wave_buffer->start_sample_offset) - buffer_offset;
+ const auto start_offset = (wave_buffer->start_sample_offset + buffer_offset) * channel_count;
+ const auto buffer_pos = wave_buffer->buffer_address + start_offset;
+
+ s16* buffer_data = reinterpret_cast<s16*>(memory.GetPointer(buffer_pos));
+
+ const auto samples_processed = std::min(sample_count, samples_remaining);
+
+ // Fast path
+ if (channel_count == 1) {
+ for (std::ptrdiff_t i = 0; i < samples_processed; i++) {
+ output_buffer[i] = buffer_data[i];
+ }
+ } else {
+ for (std::ptrdiff_t i = 0; i < samples_processed; i++) {
+ output_buffer[i] = buffer_data[i * channel_count + channel];
+ }
+ }
+
+ return samples_processed;
+}
+
+void VoiceContext::SortInfo() {
+ for (std::size_t i = 0; i < voice_count; i++) {
+ sorted_voice_info[i] = &voice_info[i];
+ }
+
+ std::sort(sorted_voice_info.begin(), sorted_voice_info.end(),
+ [](const ServerVoiceInfo* lhs, const ServerVoiceInfo* rhs) {
+ const auto& lhs_in = lhs->GetInParams();
+ const auto& rhs_in = rhs->GetInParams();
+ // Sort by priority
+ if (lhs_in.priority != rhs_in.priority) {
+ return lhs_in.priority > rhs_in.priority;
+ } else {
+ // If the priorities match, sort by sorting order
+ return lhs_in.sorting_order > rhs_in.sorting_order;
+ }
+ });
+}
+
+void VoiceContext::UpdateStateByDspShared() {
+ voice_states = dsp_voice_states;
+}
+
+} // namespace AudioCore
diff --git a/src/audio_core/voice_context.h b/src/audio_core/voice_context.h
new file mode 100644
index 000000000..59d3d7dfb
--- /dev/null
+++ b/src/audio_core/voice_context.h
@@ -0,0 +1,296 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include "audio_core/algorithm/interpolate.h"
+#include "audio_core/codec.h"
+#include "audio_core/common.h"
+#include "common/bit_field.h"
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace AudioCore {
+
+class BehaviorInfo;
+class VoiceContext;
+
+enum class SampleFormat : u8 {
+ Invalid = 0,
+ Pcm8 = 1,
+ Pcm16 = 2,
+ Pcm24 = 3,
+ Pcm32 = 4,
+ PcmFloat = 5,
+ Adpcm = 6,
+};
+
+enum class PlayState : u8 {
+ Started = 0,
+ Stopped = 1,
+ Paused = 2,
+};
+
+enum class ServerPlayState {
+ Play = 0,
+ Stop = 1,
+ RequestStop = 2,
+ Paused = 3,
+};
+
+struct BiquadFilterParameter {
+ bool enabled{};
+ INSERT_PADDING_BYTES(1);
+ std::array<s16, 3> numerator{};
+ std::array<s16, 2> denominator{};
+};
+static_assert(sizeof(BiquadFilterParameter) == 0xc, "BiquadFilterParameter is an invalid size");
+
+struct WaveBuffer {
+ u64_le buffer_address{};
+ u64_le buffer_size{};
+ s32_le start_sample_offset{};
+ s32_le end_sample_offset{};
+ u8 is_looping{};
+ u8 end_of_stream{};
+ u8 sent_to_server{};
+ INSERT_PADDING_BYTES(5);
+ u64 context_address{};
+ u64 context_size{};
+ INSERT_PADDING_BYTES(8);
+};
+static_assert(sizeof(WaveBuffer) == 0x38, "WaveBuffer is an invalid size");
+
+struct ServerWaveBuffer {
+ VAddr buffer_address{};
+ std::size_t buffer_size{};
+ s32 start_sample_offset{};
+ s32 end_sample_offset{};
+ bool is_looping{};
+ bool end_of_stream{};
+ VAddr context_address{};
+ std::size_t context_size{};
+ bool sent_to_dsp{true};
+};
+
+struct BehaviorFlags {
+ BitField<0, 1, u16> is_played_samples_reset_at_loop_point;
+ BitField<1, 1, u16> is_pitch_and_src_skipped;
+};
+static_assert(sizeof(BehaviorFlags) == 0x4, "BehaviorFlags is an invalid size");
+
+struct ADPCMContext {
+ u16 header{};
+ s16 yn1{};
+ s16 yn2{};
+};
+static_assert(sizeof(ADPCMContext) == 0x6, "ADPCMContext is an invalid size");
+
+struct VoiceState {
+ s64 played_sample_count{};
+ s32 offset{};
+ s32 wave_buffer_index{};
+ std::array<bool, AudioCommon::MAX_WAVE_BUFFERS> is_wave_buffer_valid{};
+ s32 wave_buffer_consumed{};
+ std::array<s32, AudioCommon::MAX_SAMPLE_HISTORY> sample_history{};
+ s32 fraction{};
+ VAddr context_address{};
+ Codec::ADPCM_Coeff coeff{};
+ ADPCMContext context{};
+ std::array<s64, 2> biquad_filter_state{};
+ std::array<s32, AudioCommon::MAX_MIX_BUFFERS> previous_samples{};
+ u32 external_context_size{};
+ bool is_external_context_used{};
+ bool voice_dropped{};
+};
+
+class VoiceChannelResource {
+public:
+ struct InParams {
+ s32_le id{};
+ std::array<float_le, AudioCommon::MAX_MIX_BUFFERS> mix_volume{};
+ bool in_use{};
+ INSERT_PADDING_BYTES(11);
+ };
+ static_assert(sizeof(VoiceChannelResource::InParams) == 0x70, "InParams is an invalid size");
+};
+
+class ServerVoiceChannelResource {
+public:
+ explicit ServerVoiceChannelResource(s32 id);
+ ~ServerVoiceChannelResource();
+
+ bool InUse() const;
+ float GetCurrentMixVolumeAt(std::size_t i) const;
+ float GetLastMixVolumeAt(std::size_t i) const;
+ void Update(VoiceChannelResource::InParams& in_params);
+ void UpdateLastMixVolumes();
+
+ const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& GetCurrentMixVolume() const;
+ const std::array<float, AudioCommon::MAX_MIX_BUFFERS>& GetLastMixVolume() const;
+
+private:
+ s32 id{};
+ std::array<float, AudioCommon::MAX_MIX_BUFFERS> mix_volume{};
+ std::array<float, AudioCommon::MAX_MIX_BUFFERS> last_mix_volume{};
+ bool in_use{};
+};
+
+class VoiceInfo {
+public:
+ struct InParams {
+ s32_le id{};
+ u32_le node_id{};
+ u8 is_new{};
+ u8 is_in_use{};
+ PlayState play_state{};
+ SampleFormat sample_format{};
+ s32_le sample_rate{};
+ s32_le priority{};
+ s32_le sorting_order{};
+ s32_le channel_count{};
+ float_le pitch{};
+ float_le volume{};
+ std::array<BiquadFilterParameter, 2> biquad_filter{};
+ s32_le wave_buffer_count{};
+ s16_le wave_buffer_head{};
+ INSERT_PADDING_BYTES(6);
+ u64_le additional_params_address{};
+ u64_le additional_params_size{};
+ s32_le mix_id{};
+ s32_le splitter_info_id{};
+ std::array<WaveBuffer, 4> wave_buffer{};
+ std::array<u32_le, 6> voice_channel_resource_ids{};
+ // TODO(ogniK): Remaining flags
+ u8 is_voice_drop_flag_clear_requested{};
+ u8 wave_buffer_flush_request_count{};
+ INSERT_PADDING_BYTES(2);
+ BehaviorFlags behavior_flags{};
+ INSERT_PADDING_BYTES(16);
+ };
+ static_assert(sizeof(VoiceInfo::InParams) == 0x170, "InParams is an invalid size");
+
+ struct OutParams {
+ u64_le played_sample_count{};
+ u32_le wave_buffer_consumed{};
+ u8 voice_dropped{};
+ INSERT_PADDING_BYTES(3);
+ };
+ static_assert(sizeof(VoiceInfo::OutParams) == 0x10, "OutParams is an invalid size");
+};
+
+class ServerVoiceInfo {
+public:
+ struct InParams {
+ bool in_use{};
+ bool is_new{};
+ bool should_depop{};
+ SampleFormat sample_format{};
+ s32 sample_rate{};
+ s32 channel_count{};
+ s32 id{};
+ s32 node_id{};
+ s32 mix_id{};
+ ServerPlayState current_playstate{};
+ ServerPlayState last_playstate{};
+ s32 priority{};
+ s32 sorting_order{};
+ float pitch{};
+ float volume{};
+ float last_volume{};
+ std::array<BiquadFilterParameter, AudioCommon::MAX_BIQUAD_FILTERS> biquad_filter{};
+ s32 wave_buffer_count{};
+ s16 wave_bufffer_head{};
+ INSERT_PADDING_BYTES(2);
+ BehaviorFlags behavior_flags{};
+ VAddr additional_params_address{};
+ std::size_t additional_params_size{};
+ std::array<ServerWaveBuffer, AudioCommon::MAX_WAVE_BUFFERS> wave_buffer{};
+ std::array<s32, AudioCommon::MAX_CHANNEL_COUNT> voice_channel_resource_id{};
+ s32 splitter_info_id{};
+ u8 wave_buffer_flush_request_count{};
+ bool voice_drop_flag{};
+ bool buffer_mapped{};
+ std::array<bool, AudioCommon::MAX_BIQUAD_FILTERS> was_biquad_filter_enabled{};
+ };
+
+ struct OutParams {
+ s64 played_sample_count{};
+ s32 wave_buffer_consumed{};
+ };
+
+ ServerVoiceInfo();
+ ~ServerVoiceInfo();
+ void Initialize();
+ void UpdateParameters(const VoiceInfo::InParams& voice_in, BehaviorInfo& behavior_info);
+ void UpdateWaveBuffers(const VoiceInfo::InParams& voice_in,
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states,
+ BehaviorInfo& behavior_info);
+ void UpdateWaveBuffer(ServerWaveBuffer& out_wavebuffer, const WaveBuffer& in_wave_buffer,
+ SampleFormat sample_format, bool is_buffer_valid,
+ BehaviorInfo& behavior_info);
+ void WriteOutStatus(VoiceInfo::OutParams& voice_out, VoiceInfo::InParams& voice_in,
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& voice_states);
+
+ const InParams& GetInParams() const;
+ InParams& GetInParams();
+
+ const OutParams& GetOutParams() const;
+ OutParams& GetOutParams();
+
+ bool ShouldSkip() const;
+ bool UpdateForCommandGeneration(VoiceContext& voice_context);
+ void ResetResources(VoiceContext& voice_context);
+ bool UpdateParametersForCommandGeneration(
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states);
+ void FlushWaveBuffers(u8 flush_count,
+ std::array<VoiceState*, AudioCommon::MAX_CHANNEL_COUNT>& dsp_voice_states,
+ s32 channel_count);
+
+private:
+ std::vector<s16> stored_samples;
+ InParams in_params{};
+ OutParams out_params{};
+
+ bool HasValidWaveBuffer(const VoiceState* state) const;
+};
+
+class VoiceContext {
+public:
+ VoiceContext(std::size_t voice_count);
+ ~VoiceContext();
+
+ std::size_t GetVoiceCount() const;
+ ServerVoiceChannelResource& GetChannelResource(std::size_t i);
+ const ServerVoiceChannelResource& GetChannelResource(std::size_t i) const;
+ VoiceState& GetState(std::size_t i);
+ const VoiceState& GetState(std::size_t i) const;
+ VoiceState& GetDspSharedState(std::size_t i);
+ const VoiceState& GetDspSharedState(std::size_t i) const;
+ ServerVoiceInfo& GetInfo(std::size_t i);
+ const ServerVoiceInfo& GetInfo(std::size_t i) const;
+ ServerVoiceInfo& GetSortedInfo(std::size_t i);
+ const ServerVoiceInfo& GetSortedInfo(std::size_t i) const;
+
+ s32 DecodePcm16(s32* output_buffer, ServerWaveBuffer* wave_buffer, s32 channel,
+ s32 channel_count, s32 buffer_offset, s32 sample_count,
+ Core::Memory::Memory& memory);
+ void SortInfo();
+ void UpdateStateByDspShared();
+
+private:
+ std::size_t voice_count{};
+ std::vector<ServerVoiceChannelResource> voice_channel_resources{};
+ std::vector<VoiceState> voice_states{};
+ std::vector<VoiceState> dsp_voice_states{};
+ std::vector<ServerVoiceInfo> voice_info{};
+ std::vector<ServerVoiceInfo*> sorted_voice_info{};
+};
+
+} // namespace AudioCore
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 5d54516eb..d20e6c3b5 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -102,6 +102,7 @@ add_library(common STATIC
atomic_ops.h
detached_tasks.cpp
detached_tasks.h
+ bit_cast.h
bit_field.h
bit_util.h
cityhash.cpp
@@ -150,6 +151,8 @@ add_library(common STATIC
scope_exit.h
spin_lock.cpp
spin_lock.h
+ stream.cpp
+ stream.h
string_util.cpp
string_util.h
swap.h
@@ -188,8 +191,29 @@ if(ARCHITECTURE_x86_64)
)
endif()
+if (MSVC)
+ target_compile_definitions(common PRIVATE
+ # The standard library doesn't provide any replacement for codecvt yet
+ # so we can disable this deprecation warning for the time being.
+ _SILENCE_CXX17_CODECVT_HEADER_DEPRECATION_WARNING
+ )
+ target_compile_options(common PRIVATE
+ /W4
+ /WX
+ )
+else()
+ target_compile_options(common PRIVATE
+ -Werror
+ )
+endif()
+
create_target_directory_groups(common)
find_package(Boost 1.71 COMPONENTS context headers REQUIRED)
target_link_libraries(common PUBLIC ${Boost_LIBRARIES} fmt::fmt microprofile)
-target_link_libraries(common PRIVATE lz4::lz4 zstd::zstd xbyak)
+target_link_libraries(common PRIVATE lz4::lz4 xbyak)
+if (MSVC)
+ target_link_libraries(common PRIVATE zstd::zstd)
+else()
+ target_link_libraries(common PRIVATE zstd)
+endif()
diff --git a/src/common/assert.h b/src/common/assert.h
index 5b67c5c52..06d7b5612 100644
--- a/src/common/assert.h
+++ b/src/common/assert.h
@@ -17,11 +17,12 @@
// enough for our purposes.
template <typename Fn>
#if defined(_MSC_VER)
-__declspec(noinline, noreturn)
+[[msvc::noinline, noreturn]]
#elif defined(__GNUC__)
- __attribute__((noinline, noreturn, cold))
+[[gnu::cold, gnu::noinline, noreturn]]
#endif
- static void assert_noinline_call(const Fn& fn) {
+static void
+assert_noinline_call(const Fn& fn) {
fn();
Crash();
exit(1); // Keeps GCC's mouth shut about this actually returning
diff --git a/src/common/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 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstring>
+#include <type_traits>
+
+namespace Common {
+
+template <typename To, typename From>
+[[nodiscard]] std::enable_if_t<sizeof(To) == sizeof(From) && std::is_trivially_copyable_v<From> &&
+ std::is_trivially_copyable_v<To>,
+ To>
+BitCast(const From& src) noexcept {
+ To dst;
+ std::memcpy(&dst, &src, sizeof(To));
+ return dst;
+}
+
+} // namespace Common
diff --git a/src/common/color.h b/src/common/color.h
index 381d6332e..bbcac858e 100644
--- a/src/common/color.h
+++ b/src/common/color.h
@@ -10,7 +10,7 @@
#include "common/swap.h"
#include "common/vector_math.h"
-namespace Color {
+namespace Common::Color {
/// Convert a 1-bit color component to 8 bit
[[nodiscard]] constexpr u8 Convert1To8(u8 value) {
@@ -268,4 +268,4 @@ inline void EncodeX24S8(u8 stencil, u8* bytes) {
bytes[3] = stencil;
}
-} // namespace Color
+} // namespace Common::Color
diff --git a/src/common/common_funcs.h b/src/common/common_funcs.h
index 98421bced..367b6bf6e 100644
--- a/src/common/common_funcs.h
+++ b/src/common/common_funcs.h
@@ -64,14 +64,20 @@ __declspec(dllimport) void __stdcall DebugBreak(void);
using T = std::underlying_type_t<type>; \
return static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
} \
- constexpr type& operator|=(type& a, type b) noexcept { \
+ [[nodiscard]] constexpr type operator^(type a, type b) noexcept { \
using T = std::underlying_type_t<type>; \
- a = static_cast<type>(static_cast<T>(a) | static_cast<T>(b)); \
+ return static_cast<type>(static_cast<T>(a) ^ static_cast<T>(b)); \
+ } \
+ constexpr type& operator|=(type& a, type b) noexcept { \
+ a = a | b; \
return a; \
} \
constexpr type& operator&=(type& a, type b) noexcept { \
- using T = std::underlying_type_t<type>; \
- a = static_cast<type>(static_cast<T>(a) & static_cast<T>(b)); \
+ a = a & b; \
+ return a; \
+ } \
+ constexpr type& operator^=(type& a, type b) noexcept { \
+ a = a ^ b; \
return a; \
} \
[[nodiscard]] constexpr type operator~(type key) noexcept { \
diff --git a/src/common/fiber.cpp b/src/common/fiber.cpp
index 1c1d09ccb..3e3029cd1 100644
--- a/src/common/fiber.cpp
+++ b/src/common/fiber.cpp
@@ -4,6 +4,8 @@
#include "common/assert.h"
#include "common/fiber.h"
+#include "common/spin_lock.h"
+
#if defined(_WIN32) || defined(WIN32)
#include <windows.h>
#else
@@ -14,18 +16,45 @@ namespace Common {
constexpr std::size_t default_stack_size = 256 * 1024; // 256kb
-#if defined(_WIN32) || defined(WIN32)
-
struct Fiber::FiberImpl {
+ SpinLock guard{};
+ std::function<void(void*)> entry_point;
+ std::function<void(void*)> rewind_point;
+ void* rewind_parameter{};
+ void* start_parameter{};
+ std::shared_ptr<Fiber> previous_fiber;
+ bool is_thread_fiber{};
+ bool released{};
+
+#if defined(_WIN32) || defined(WIN32)
LPVOID handle = nullptr;
LPVOID rewind_handle = nullptr;
+#else
+ alignas(64) std::array<u8, default_stack_size> stack;
+ alignas(64) std::array<u8, default_stack_size> rewind_stack;
+ u8* stack_limit;
+ u8* rewind_stack_limit;
+ boost::context::detail::fcontext_t context;
+ boost::context::detail::fcontext_t rewind_context;
+#endif
};
+void Fiber::SetStartParameter(void* new_parameter) {
+ impl->start_parameter = new_parameter;
+}
+
+void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param) {
+ impl->rewind_point = std::move(rewind_func);
+ impl->rewind_parameter = rewind_param;
+}
+
+#if defined(_WIN32) || defined(WIN32)
+
void Fiber::Start() {
- ASSERT(previous_fiber != nullptr);
- previous_fiber->guard.unlock();
- previous_fiber.reset();
- entry_point(start_parameter);
+ ASSERT(impl->previous_fiber != nullptr);
+ impl->previous_fiber->impl->guard.unlock();
+ impl->previous_fiber.reset();
+ impl->entry_point(impl->start_parameter);
UNREACHABLE();
}
@@ -34,99 +63,86 @@ void Fiber::OnRewind() {
DeleteFiber(impl->handle);
impl->handle = impl->rewind_handle;
impl->rewind_handle = nullptr;
- rewind_point(rewind_parameter);
+ impl->rewind_point(impl->rewind_parameter);
UNREACHABLE();
}
void Fiber::FiberStartFunc(void* fiber_parameter) {
- auto fiber = static_cast<Fiber*>(fiber_parameter);
+ auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->Start();
}
void Fiber::RewindStartFunc(void* fiber_parameter) {
- auto fiber = static_cast<Fiber*>(fiber_parameter);
+ auto* fiber = static_cast<Fiber*>(fiber_parameter);
fiber->OnRewind();
}
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
- : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
- impl = std::make_unique<FiberImpl>();
+ : impl{std::make_unique<FiberImpl>()} {
+ impl->entry_point = std::move(entry_point_func);
+ impl->start_parameter = start_parameter;
impl->handle = CreateFiber(default_stack_size, &FiberStartFunc, this);
}
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
- if (released) {
+ if (impl->released) {
return;
}
// Make sure the Fiber is not being used
- const bool locked = guard.try_lock();
+ const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
- guard.unlock();
+ impl->guard.unlock();
}
DeleteFiber(impl->handle);
}
void Fiber::Exit() {
- ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
- if (!is_thread_fiber) {
+ ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
+ if (!impl->is_thread_fiber) {
return;
}
ConvertFiberToThread();
- guard.unlock();
- released = true;
-}
-
-void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
- rewind_point = std::move(rewind_func);
- rewind_parameter = start_parameter;
+ impl->guard.unlock();
+ impl->released = true;
}
void Fiber::Rewind() {
- ASSERT(rewind_point);
+ ASSERT(impl->rewind_point);
ASSERT(impl->rewind_handle == nullptr);
impl->rewind_handle = CreateFiber(default_stack_size, &RewindStartFunc, this);
SwitchToFiber(impl->rewind_handle);
}
-void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
+void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
- to->guard.lock();
- to->previous_fiber = from;
+ to->impl->guard.lock();
+ to->impl->previous_fiber = from;
SwitchToFiber(to->impl->handle);
- ASSERT(from->previous_fiber != nullptr);
- from->previous_fiber->guard.unlock();
- from->previous_fiber.reset();
+ ASSERT(from->impl->previous_fiber != nullptr);
+ from->impl->previous_fiber->impl->guard.unlock();
+ from->impl->previous_fiber.reset();
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
- fiber->guard.lock();
+ fiber->impl->guard.lock();
fiber->impl->handle = ConvertThreadToFiber(nullptr);
- fiber->is_thread_fiber = true;
+ fiber->impl->is_thread_fiber = true;
return fiber;
}
#else
-struct Fiber::FiberImpl {
- alignas(64) std::array<u8, default_stack_size> stack;
- alignas(64) std::array<u8, default_stack_size> rewind_stack;
- u8* stack_limit;
- u8* rewind_stack_limit;
- boost::context::detail::fcontext_t context;
- boost::context::detail::fcontext_t rewind_context;
-};
-
void Fiber::Start(boost::context::detail::transfer_t& transfer) {
- ASSERT(previous_fiber != nullptr);
- previous_fiber->impl->context = transfer.fctx;
- previous_fiber->guard.unlock();
- previous_fiber.reset();
- entry_point(start_parameter);
+ ASSERT(impl->previous_fiber != nullptr);
+ impl->previous_fiber->impl->context = transfer.fctx;
+ impl->previous_fiber->impl->guard.unlock();
+ impl->previous_fiber.reset();
+ impl->entry_point(impl->start_parameter);
UNREACHABLE();
}
@@ -137,23 +153,24 @@ void Fiber::OnRewind([[maybe_unused]] boost::context::detail::transfer_t& transf
u8* tmp = impl->stack_limit;
impl->stack_limit = impl->rewind_stack_limit;
impl->rewind_stack_limit = tmp;
- rewind_point(rewind_parameter);
+ impl->rewind_point(impl->rewind_parameter);
UNREACHABLE();
}
void Fiber::FiberStartFunc(boost::context::detail::transfer_t transfer) {
- auto fiber = static_cast<Fiber*>(transfer.data);
+ auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->Start(transfer);
}
void Fiber::RewindStartFunc(boost::context::detail::transfer_t transfer) {
- auto fiber = static_cast<Fiber*>(transfer.data);
+ auto* fiber = static_cast<Fiber*>(transfer.data);
fiber->OnRewind(transfer);
}
Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_parameter)
- : entry_point{std::move(entry_point_func)}, start_parameter{start_parameter} {
- impl = std::make_unique<FiberImpl>();
+ : impl{std::make_unique<FiberImpl>()} {
+ impl->entry_point = std::move(entry_point_func);
+ impl->start_parameter = start_parameter;
impl->stack_limit = impl->stack.data();
impl->rewind_stack_limit = impl->rewind_stack.data();
u8* stack_base = impl->stack_limit + default_stack_size;
@@ -161,37 +178,31 @@ Fiber::Fiber(std::function<void(void*)>&& entry_point_func, void* start_paramete
boost::context::detail::make_fcontext(stack_base, impl->stack.size(), FiberStartFunc);
}
-void Fiber::SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter) {
- rewind_point = std::move(rewind_func);
- rewind_parameter = start_parameter;
-}
-
Fiber::Fiber() : impl{std::make_unique<FiberImpl>()} {}
Fiber::~Fiber() {
- if (released) {
+ if (impl->released) {
return;
}
// Make sure the Fiber is not being used
- const bool locked = guard.try_lock();
+ const bool locked = impl->guard.try_lock();
ASSERT_MSG(locked, "Destroying a fiber that's still running");
if (locked) {
- guard.unlock();
+ impl->guard.unlock();
}
}
void Fiber::Exit() {
-
- ASSERT_MSG(is_thread_fiber, "Exitting non main thread fiber");
- if (!is_thread_fiber) {
+ ASSERT_MSG(impl->is_thread_fiber, "Exitting non main thread fiber");
+ if (!impl->is_thread_fiber) {
return;
}
- guard.unlock();
- released = true;
+ impl->guard.unlock();
+ impl->released = true;
}
void Fiber::Rewind() {
- ASSERT(rewind_point);
+ ASSERT(impl->rewind_point);
ASSERT(impl->rewind_context == nullptr);
u8* stack_base = impl->rewind_stack_limit + default_stack_size;
impl->rewind_context =
@@ -199,22 +210,22 @@ void Fiber::Rewind() {
boost::context::detail::jump_fcontext(impl->rewind_context, this);
}
-void Fiber::YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to) {
+void Fiber::YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to) {
ASSERT_MSG(from != nullptr, "Yielding fiber is null!");
ASSERT_MSG(to != nullptr, "Next fiber is null!");
- to->guard.lock();
- to->previous_fiber = from;
+ to->impl->guard.lock();
+ to->impl->previous_fiber = from;
auto transfer = boost::context::detail::jump_fcontext(to->impl->context, to.get());
- ASSERT(from->previous_fiber != nullptr);
- from->previous_fiber->impl->context = transfer.fctx;
- from->previous_fiber->guard.unlock();
- from->previous_fiber.reset();
+ ASSERT(from->impl->previous_fiber != nullptr);
+ from->impl->previous_fiber->impl->context = transfer.fctx;
+ from->impl->previous_fiber->impl->guard.unlock();
+ from->impl->previous_fiber.reset();
}
std::shared_ptr<Fiber> Fiber::ThreadToFiber() {
std::shared_ptr<Fiber> fiber = std::shared_ptr<Fiber>{new Fiber()};
- fiber->guard.lock();
- fiber->is_thread_fiber = true;
+ fiber->impl->guard.lock();
+ fiber->impl->is_thread_fiber = true;
return fiber;
}
diff --git a/src/common/fiber.h b/src/common/fiber.h
index 89dde5e36..5323e8579 100644
--- a/src/common/fiber.h
+++ b/src/common/fiber.h
@@ -7,9 +7,6 @@
#include <functional>
#include <memory>
-#include "common/common_types.h"
-#include "common/spin_lock.h"
-
#if !defined(_WIN32) && !defined(WIN32)
namespace boost::context::detail {
struct transfer_t;
@@ -46,10 +43,10 @@ public:
/// Yields control from Fiber 'from' to Fiber 'to'
/// Fiber 'from' must be the currently running fiber.
- static void YieldTo(std::shared_ptr<Fiber>& from, std::shared_ptr<Fiber>& to);
+ static void YieldTo(std::shared_ptr<Fiber> from, std::shared_ptr<Fiber> to);
[[nodiscard]] static std::shared_ptr<Fiber> ThreadToFiber();
- void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* start_parameter);
+ void SetRewindPoint(std::function<void(void*)>&& rewind_func, void* rewind_param);
void Rewind();
@@ -57,9 +54,7 @@ public:
void Exit();
/// Changes the start parameter of the fiber. Has no effect if the fiber already started
- void SetStartParameter(void* new_parameter) {
- start_parameter = new_parameter;
- }
+ void SetStartParameter(void* new_parameter);
private:
Fiber();
@@ -77,16 +72,7 @@ private:
#endif
struct FiberImpl;
-
- SpinLock guard{};
- std::function<void(void*)> entry_point;
- std::function<void(void*)> rewind_point;
- void* rewind_parameter{};
- void* start_parameter{};
- std::shared_ptr<Fiber> previous_fiber;
std::unique_ptr<FiberImpl> impl;
- bool is_thread_fiber{};
- bool released{};
};
} // namespace Common
diff --git a/src/common/file_util.cpp b/src/common/file_util.cpp
index 16c3713e0..18fbfa25b 100644
--- a/src/common/file_util.cpp
+++ b/src/common/file_util.cpp
@@ -472,13 +472,14 @@ u64 ScanDirectoryTree(const std::string& directory, FSTEntry& parent_entry,
}
bool DeleteDirRecursively(const std::string& directory, unsigned int recursion) {
- const auto callback = [recursion](u64* num_entries_out, const std::string& directory,
- const std::string& virtual_name) -> bool {
- std::string new_path = directory + DIR_SEP_CHR + virtual_name;
+ const auto callback = [recursion](u64*, const std::string& directory,
+ const std::string& virtual_name) {
+ const std::string new_path = directory + DIR_SEP_CHR + virtual_name;
if (IsDirectory(new_path)) {
- if (recursion == 0)
+ if (recursion == 0) {
return false;
+ }
return DeleteDirRecursively(new_path, recursion - 1);
}
return Delete(new_path);
@@ -492,7 +493,8 @@ bool DeleteDirRecursively(const std::string& directory, unsigned int recursion)
return true;
}
-void CopyDir(const std::string& source_path, const std::string& dest_path) {
+void CopyDir([[maybe_unused]] const std::string& source_path,
+ [[maybe_unused]] const std::string& dest_path) {
#ifndef _WIN32
if (source_path == dest_path) {
return;
@@ -553,7 +555,7 @@ std::optional<std::string> GetCurrentDir() {
std::string strDir = dir;
#endif
free(dir);
- return std::move(strDir);
+ return strDir;
}
bool SetCurrentDir(const std::string& directory) {
@@ -772,21 +774,23 @@ std::size_t ReadFileToString(bool text_file, const std::string& filename, std::s
void SplitFilename83(const std::string& filename, std::array<char, 9>& short_name,
std::array<char, 4>& extension) {
- const std::string forbidden_characters = ".\"/\\[]:;=, ";
+ static constexpr std::string_view forbidden_characters = ".\"/\\[]:;=, ";
// On a FAT32 partition, 8.3 names are stored as a 11 bytes array, filled with spaces.
short_name = {{' ', ' ', ' ', ' ', ' ', ' ', ' ', ' ', '\0'}};
extension = {{' ', ' ', ' ', '\0'}};
- std::string::size_type point = filename.rfind('.');
- if (point == filename.size() - 1)
+ auto point = filename.rfind('.');
+ if (point == filename.size() - 1) {
point = filename.rfind('.', point);
+ }
// Get short name.
int j = 0;
for (char letter : filename.substr(0, point)) {
- if (forbidden_characters.find(letter, 0) != std::string::npos)
+ if (forbidden_characters.find(letter, 0) != std::string::npos) {
continue;
+ }
if (j == 8) {
// TODO(Link Mauve): also do that for filenames containing a space.
// TODO(Link Mauve): handle multiple files having the same short name.
@@ -794,14 +798,15 @@ void SplitFilename83(const std::string& filename, std::array<char, 9>& short_nam
short_name[7] = '1';
break;
}
- short_name[j++] = toupper(letter);
+ short_name[j++] = static_cast<char>(std::toupper(letter));
}
// Get extension.
if (point != std::string::npos) {
j = 0;
- for (char letter : filename.substr(point + 1, 3))
- extension[j++] = toupper(letter);
+ for (char letter : filename.substr(point + 1, 3)) {
+ extension[j++] = static_cast<char>(std::toupper(letter));
+ }
}
}
diff --git a/src/common/file_util.h b/src/common/file_util.h
index 8b587320f..840cde2a6 100644
--- a/src/common/file_util.h
+++ b/src/common/file_util.h
@@ -232,7 +232,7 @@ public:
void Swap(IOFile& other) noexcept;
- [[nodiscard]] bool Open(const std::string& filename, const char openmode[], int flags = 0);
+ bool Open(const std::string& filename, const char openmode[], int flags = 0);
bool Close();
template <typename T>
diff --git a/src/common/hex_util.h b/src/common/hex_util.h
index 120f1a5e6..a8d414fb8 100644
--- a/src/common/hex_util.h
+++ b/src/common/hex_util.h
@@ -16,14 +16,14 @@ namespace Common {
[[nodiscard]] constexpr u8 ToHexNibble(char c) {
if (c >= 65 && c <= 70) {
- return c - 55;
+ return static_cast<u8>(c - 55);
}
if (c >= 97 && c <= 102) {
- return c - 87;
+ return static_cast<u8>(c - 87);
}
- return c - 48;
+ return static_cast<u8>(c - 48);
}
[[nodiscard]] std::vector<u8> HexStringToVector(std::string_view str, bool little_endian);
@@ -33,11 +33,11 @@ template <std::size_t Size, bool le = false>
std::array<u8, Size> out{};
if constexpr (le) {
for (std::size_t i = 2 * Size - 2; i <= 2 * Size; i -= 2) {
- out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
}
} else {
for (std::size_t i = 0; i < 2 * Size; i += 2) {
- out[i / 2] = (ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]);
+ out[i / 2] = static_cast<u8>((ToHexNibble(str[i]) << 4) | ToHexNibble(str[i + 1]));
}
}
return out;
diff --git a/src/common/logging/backend.cpp b/src/common/logging/backend.cpp
index fdb2e52fa..631f64d05 100644
--- a/src/common/logging/backend.cpp
+++ b/src/common/logging/backend.cpp
@@ -232,6 +232,7 @@ void DebuggerBackend::Write(const Entry& entry) {
SUB(Service, NPNS) \
SUB(Service, NS) \
SUB(Service, NVDRV) \
+ SUB(Service, OLSC) \
SUB(Service, PCIE) \
SUB(Service, PCTL) \
SUB(Service, PCV) \
@@ -284,7 +285,6 @@ const char* GetLogClassName(Class log_class) {
case Class::Count:
break;
}
- UNREACHABLE();
return "Invalid";
}
@@ -303,7 +303,6 @@ const char* GetLevelName(Level log_level) {
break;
}
#undef LVL
- UNREACHABLE();
return "Invalid";
}
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 {
Service_NPNS, ///< The NPNS service
Service_NS, ///< The NS services
Service_NVDRV, ///< The NVDRV (Nvidia driver) service
+ Service_OLSC, ///< The OLSC service
Service_PCIE, ///< The PCIe service
Service_PCTL, ///< The PCTL (Parental control) service
Service_PCV, ///< The PCV service
diff --git a/src/common/math_util.h b/src/common/math_util.h
index cc35c90ee..4c38d8040 100644
--- a/src/common/math_util.h
+++ b/src/common/math_util.h
@@ -9,7 +9,7 @@
namespace Common {
-constexpr float PI = 3.14159265f;
+constexpr float PI = 3.1415926535f;
template <class T>
struct Rectangle {
@@ -20,14 +20,14 @@ struct Rectangle {
constexpr Rectangle() = default;
- constexpr Rectangle(T left, T top, T right, T bottom)
- : left(left), top(top), right(right), bottom(bottom) {}
+ constexpr Rectangle(T left_, T top_, T right_, T bottom_)
+ : left(left_), top(top_), right(right_), bottom(bottom_) {}
[[nodiscard]] T GetWidth() const {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(right - left);
} else {
- return std::abs(static_cast<std::make_signed_t<T>>(right - left));
+ return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(right - left)));
}
}
@@ -35,7 +35,7 @@ struct Rectangle {
if constexpr (std::is_floating_point_v<T>) {
return std::abs(bottom - top);
} else {
- return std::abs(static_cast<std::make_signed_t<T>>(bottom - top));
+ return static_cast<T>(std::abs(static_cast<std::make_signed_t<T>>(bottom - top)));
}
}
diff --git a/src/common/misc.cpp b/src/common/misc.cpp
index 68cb86cd1..1d5393597 100644
--- a/src/common/misc.cpp
+++ b/src/common/misc.cpp
@@ -16,16 +16,23 @@
// Call directly after the command or use the error num.
// This function might change the error code.
std::string GetLastErrorMsg() {
- static const std::size_t buff_size = 255;
+ static constexpr std::size_t buff_size = 255;
char err_str[buff_size];
#ifdef _WIN32
FormatMessageA(FORMAT_MESSAGE_FROM_SYSTEM, nullptr, GetLastError(),
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), err_str, buff_size, nullptr);
+ return std::string(err_str, buff_size);
+#elif defined(__GLIBC__) && (_GNU_SOURCE || (_POSIX_C_SOURCE < 200112L && _XOPEN_SOURCE < 600))
+ // Thread safe (GNU-specific)
+ const char* str = strerror_r(errno, err_str, buff_size);
+ return std::string(str);
#else
// Thread safe (XSI-compliant)
- strerror_r(errno, err_str, buff_size);
+ const int success = strerror_r(errno, err_str, buff_size);
+ if (success != 0) {
+ return {};
+ }
+ return std::string(err_str);
#endif
-
- return std::string(err_str, buff_size);
}
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 {
PageTable::PageTable() = default;
-PageTable::~PageTable() = default;
+PageTable::~PageTable() noexcept = default;
void PageTable::Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
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 @@
#pragma once
-#include <vector>
-
-#include <boost/icl/interval_map.hpp>
+#include <tuple>
#include "common/common_types.h"
#include "common/memory_hook.h"
@@ -51,13 +49,21 @@ struct SpecialRegion {
*/
struct PageTable {
PageTable();
- ~PageTable();
+ ~PageTable() noexcept;
+
+ PageTable(const PageTable&) = delete;
+ PageTable& operator=(const PageTable&) = delete;
+
+ PageTable(PageTable&&) noexcept = default;
+ PageTable& operator=(PageTable&&) noexcept = default;
/**
* Resizes the page table to be able to accomodate enough pages within
* a given address space.
*
* @param address_space_width_in_bits The address size width in bits.
+ * @param page_size_in_bits The page size in bits.
+ * @param has_attribute Whether or not this page has any backing attributes.
*/
void Resize(std::size_t address_space_width_in_bits, std::size_t page_size_in_bits,
bool has_attribute);
diff --git a/src/common/param_package.h b/src/common/param_package.h
index c8a70bfa9..c13e45479 100644
--- a/src/common/param_package.h
+++ b/src/common/param_package.h
@@ -19,7 +19,7 @@ public:
explicit ParamPackage(const std::string& serialized);
ParamPackage(std::initializer_list<DataType::value_type> list);
ParamPackage(const ParamPackage& other) = default;
- ParamPackage(ParamPackage&& other) = default;
+ ParamPackage(ParamPackage&& other) noexcept = default;
ParamPackage& operator=(const ParamPackage& other) = default;
ParamPackage& operator=(ParamPackage&& other) = default;
diff --git a/src/common/quaternion.h b/src/common/quaternion.h
index da44f35cd..4d0871eb4 100644
--- a/src/common/quaternion.h
+++ b/src/common/quaternion.h
@@ -36,6 +36,36 @@ public:
T length = std::sqrt(xyz.Length2() + w * w);
return {xyz / length, w / length};
}
+
+ [[nodiscard]] std::array<decltype(-T{}), 16> ToMatrix() const {
+ const T x2 = xyz[0] * xyz[0];
+ const T y2 = xyz[1] * xyz[1];
+ const T z2 = xyz[2] * xyz[2];
+
+ const T xy = xyz[0] * xyz[1];
+ const T wz = w * xyz[2];
+ const T xz = xyz[0] * xyz[2];
+ const T wy = w * xyz[1];
+ const T yz = xyz[1] * xyz[2];
+ const T wx = w * xyz[0];
+
+ return {1.0f - 2.0f * (y2 + z2),
+ 2.0f * (xy + wz),
+ 2.0f * (xz - wy),
+ 0.0f,
+ 2.0f * (xy - wz),
+ 1.0f - 2.0f * (x2 + z2),
+ 2.0f * (yz + wx),
+ 0.0f,
+ 2.0f * (xz + wy),
+ 2.0f * (yz - wx),
+ 1.0f - 2.0f * (x2 + y2),
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 0.0f,
+ 1.0f};
+ }
};
template <typename T>
diff --git a/src/common/spin_lock.h b/src/common/spin_lock.h
index 4f946a258..06ac2f5bb 100644
--- a/src/common/spin_lock.h
+++ b/src/common/spin_lock.h
@@ -15,6 +15,14 @@ namespace Common {
*/
class SpinLock {
public:
+ SpinLock() = default;
+
+ SpinLock(const SpinLock&) = delete;
+ SpinLock& operator=(const SpinLock&) = delete;
+
+ SpinLock(SpinLock&&) = delete;
+ SpinLock& operator=(SpinLock&&) = delete;
+
void lock();
void unlock();
[[nodiscard]] bool try_lock();
diff --git a/src/common/stream.cpp b/src/common/stream.cpp
new file mode 100644
index 000000000..bf0496c26
--- /dev/null
+++ b/src/common/stream.cpp
@@ -0,0 +1,47 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <stdexcept>
+#include "common/common_types.h"
+#include "common/stream.h"
+
+namespace Common {
+
+Stream::Stream() = default;
+Stream::~Stream() = default;
+
+void Stream::Seek(s32 offset, SeekOrigin origin) {
+ if (origin == SeekOrigin::SetOrigin) {
+ if (offset < 0) {
+ position = 0;
+ } else if (position >= buffer.size()) {
+ position = buffer.size();
+ } else {
+ position = offset;
+ }
+ } else if (origin == SeekOrigin::FromCurrentPos) {
+ Seek(static_cast<s32>(position) + offset, SeekOrigin::SetOrigin);
+ } else if (origin == SeekOrigin::FromEnd) {
+ Seek(static_cast<s32>(buffer.size()) - offset, SeekOrigin::SetOrigin);
+ }
+}
+
+u8 Stream::ReadByte() {
+ if (position < buffer.size()) {
+ return buffer[position++];
+ } else {
+ throw std::out_of_range("Attempting to read a byte not within the buffer range");
+ }
+}
+
+void Stream::WriteByte(u8 byte) {
+ if (position == buffer.size()) {
+ buffer.push_back(byte);
+ position++;
+ } else {
+ buffer.insert(buffer.begin() + position, byte);
+ }
+}
+
+} // namespace Common
diff --git a/src/common/stream.h b/src/common/stream.h
new file mode 100644
index 000000000..0e40692de
--- /dev/null
+++ b/src/common/stream.h
@@ -0,0 +1,56 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include "common/common_types.h"
+
+namespace Common {
+
+enum class SeekOrigin {
+ SetOrigin,
+ FromCurrentPos,
+ FromEnd,
+};
+
+class Stream {
+public:
+ /// Stream creates a bitstream and provides common functionality on the stream.
+ explicit Stream();
+ ~Stream();
+
+ Stream(const Stream&) = delete;
+ Stream& operator=(const Stream&) = delete;
+
+ Stream(Stream&&) = default;
+ Stream& operator=(Stream&&) = default;
+
+ /// Reposition bitstream "cursor" to the specified offset from origin
+ void Seek(s32 offset, SeekOrigin origin);
+
+ /// Reads next byte in the stream buffer and increments position
+ u8 ReadByte();
+
+ /// Writes byte at current position
+ void WriteByte(u8 byte);
+
+ [[nodiscard]] std::size_t GetPosition() const {
+ return position;
+ }
+
+ [[nodiscard]] std::vector<u8>& GetBuffer() {
+ return buffer;
+ }
+
+ [[nodiscard]] const std::vector<u8>& GetBuffer() const {
+ return buffer;
+ }
+
+private:
+ std::vector<u8> buffer;
+ std::size_t position{0};
+};
+
+} // namespace Common
diff --git a/src/common/string_util.cpp b/src/common/string_util.cpp
index 84883a1d3..4cba2aaa4 100644
--- a/src/common/string_util.cpp
+++ b/src/common/string_util.cpp
@@ -8,6 +8,7 @@
#include <cstdlib>
#include <locale>
#include <sstream>
+
#include "common/common_paths.h"
#include "common/logging/log.h"
#include "common/string_util.h"
@@ -21,14 +22,14 @@ namespace Common {
/// Make a string lowercase
std::string ToLower(std::string str) {
std::transform(str.begin(), str.end(), str.begin(),
- [](unsigned char c) { return std::tolower(c); });
+ [](unsigned char c) { return static_cast<char>(std::tolower(c)); });
return str;
}
/// Make a string uppercase
std::string ToUpper(std::string str) {
std::transform(str.begin(), str.end(), str.begin(),
- [](unsigned char c) { return std::toupper(c); });
+ [](unsigned char c) { return static_cast<char>(std::toupper(c)); });
return str;
}
diff --git a/src/common/thread.cpp b/src/common/thread.cpp
index 8e5935e6a..d2c1ac60d 100644
--- a/src/common/thread.cpp
+++ b/src/common/thread.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/common_funcs.h"
+#include "common/logging/log.h"
#include "common/thread.h"
#ifdef __APPLE__
#include <mach/mach.h>
@@ -19,6 +21,8 @@
#include <unistd.h>
#endif
+#include <string>
+
#ifdef __FreeBSD__
#define cpu_set_t cpuset_t
#endif
@@ -110,6 +114,14 @@ void SetCurrentThreadName(const char* name) {
pthread_set_name_np(pthread_self(), name);
#elif defined(__NetBSD__)
pthread_setname_np(pthread_self(), "%s", (void*)name);
+#elif defined(__linux__)
+ // Linux limits thread names to 15 characters and will outright reject any
+ // attempt to set a longer name with ERANGE.
+ std::string truncated(name, std::min(strlen(name), static_cast<size_t>(15)));
+ if (int e = pthread_setname_np(pthread_self(), truncated.c_str())) {
+ errno = e;
+ LOG_ERROR(Common, "Failed to set thread name to '{}': {}", truncated, GetLastErrorMsg());
+ }
#else
pthread_setname_np(pthread_self(), name);
#endif
diff --git a/src/common/thread.h b/src/common/thread.h
index 52b359413..a8c17c71a 100644
--- a/src/common/thread.h
+++ b/src/common/thread.h
@@ -4,6 +4,7 @@
#pragma once
+#include <atomic>
#include <chrono>
#include <condition_variable>
#include <cstddef>
@@ -25,13 +26,13 @@ public:
void Wait() {
std::unique_lock lk{mutex};
- condvar.wait(lk, [&] { return is_set; });
+ condvar.wait(lk, [&] { return is_set.load(); });
is_set = false;
}
bool WaitFor(const std::chrono::nanoseconds& time) {
std::unique_lock lk{mutex};
- if (!condvar.wait_for(lk, time, [this] { return is_set; }))
+ if (!condvar.wait_for(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
@@ -40,7 +41,7 @@ public:
template <class Clock, class Duration>
bool WaitUntil(const std::chrono::time_point<Clock, Duration>& time) {
std::unique_lock lk{mutex};
- if (!condvar.wait_until(lk, time, [this] { return is_set; }))
+ if (!condvar.wait_until(lk, time, [this] { return is_set.load(); }))
return false;
is_set = false;
return true;
@@ -54,9 +55,9 @@ public:
}
private:
- bool is_set = false;
std::condition_variable condvar;
std::mutex mutex;
+ std::atomic_bool is_set{false};
};
class Barrier {
diff --git a/src/common/timer.cpp b/src/common/timer.cpp
index 2dc15e434..d17dc2a50 100644
--- a/src/common/timer.cpp
+++ b/src/common/timer.cpp
@@ -142,20 +142,18 @@ std::string Timer::GetTimeFormatted() {
// ----------------
double Timer::GetDoubleTime() {
// Get continuous timestamp
- u64 TmpSeconds = static_cast<u64>(Common::Timer::GetTimeSinceJan1970().count());
- double ms = static_cast<u64>(GetTimeMs().count()) % 1000;
+ auto tmp_seconds = static_cast<u64>(GetTimeSinceJan1970().count());
+ const auto ms = static_cast<double>(static_cast<u64>(GetTimeMs().count()) % 1000);
// Remove a few years. We only really want enough seconds to make
// sure that we are detecting actual actions, perhaps 60 seconds is
// enough really, but I leave a year of seconds anyway, in case the
// user's clock is incorrect or something like that.
- TmpSeconds = TmpSeconds - (38 * 365 * 24 * 60 * 60);
+ tmp_seconds = tmp_seconds - (38 * 365 * 24 * 60 * 60);
// Make a smaller integer that fits in the double
- u32 Seconds = static_cast<u32>(TmpSeconds);
- double TmpTime = Seconds + ms;
-
- return TmpTime;
+ const auto seconds = static_cast<u32>(tmp_seconds);
+ return seconds + ms;
}
} // Namespace Common
diff --git a/src/common/vector_math.h b/src/common/vector_math.h
index 2a0fcf541..22dba3c2d 100644
--- a/src/common/vector_math.h
+++ b/src/common/vector_math.h
@@ -87,7 +87,13 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} * V{})> operator*(const V& f) const {
- return {x * f, y * f};
+ using TV = decltype(T{} * V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
+ };
}
template <typename V>
@@ -98,7 +104,13 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec2<decltype(T{} / V{})> operator/(const V& f) const {
- return {x / f, y / f};
+ using TV = decltype(T{} / V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
+ };
}
template <typename V>
@@ -168,7 +180,10 @@ public:
template <typename T, typename V>
[[nodiscard]] constexpr Vec2<T> operator*(const V& f, const Vec2<T>& vec) {
- return Vec2<T>(f * vec.x, f * vec.y);
+ using C = std::common_type_t<T, V>;
+
+ return Vec2<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
+ static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)));
}
using Vec2f = Vec2<float>;
@@ -237,7 +252,14 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} * V{})> operator*(const V& f) const {
- return {x * f, y * f, z * f};
+ using TV = decltype(T{} * V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
+ };
}
template <typename V>
@@ -247,7 +269,14 @@ public:
}
template <typename V>
[[nodiscard]] constexpr Vec3<decltype(T{} / V{})> operator/(const V& f) const {
- return {x / f, y / f, z / f};
+ using TV = decltype(T{} / V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
+ };
}
template <typename V>
@@ -367,7 +396,11 @@ public:
template <typename T, typename V>
[[nodiscard]] constexpr Vec3<T> operator*(const V& f, const Vec3<T>& vec) {
- return Vec3<T>(f * vec.x, f * vec.y, f * vec.z);
+ using C = std::common_type_t<T, V>;
+
+ return Vec3<T>(static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.x)),
+ static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.y)),
+ static_cast<T>(static_cast<C>(f) * static_cast<C>(vec.z)));
}
template <>
@@ -446,7 +479,15 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} * V{})> operator*(const V& f) const {
- return {x * f, y * f, z * f, w * f};
+ using TV = decltype(T{} * V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) * static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(w) * static_cast<C>(f)),
+ };
}
template <typename V>
@@ -457,7 +498,15 @@ public:
template <typename V>
[[nodiscard]] constexpr Vec4<decltype(T{} / V{})> operator/(const V& f) const {
- return {x / f, y / f, z / f, w / f};
+ using TV = decltype(T{} / V{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(x) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(y) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(z) / static_cast<C>(f)),
+ static_cast<TV>(static_cast<C>(w) / static_cast<C>(f)),
+ };
}
template <typename V>
@@ -582,7 +631,15 @@ public:
template <typename T, typename V>
[[nodiscard]] constexpr Vec4<decltype(V{} * T{})> operator*(const V& f, const Vec4<T>& vec) {
- return {f * vec.x, f * vec.y, f * vec.z, f * vec.w};
+ using TV = decltype(V{} * T{});
+ using C = std::common_type_t<T, V>;
+
+ return {
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.x)),
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.y)),
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.z)),
+ static_cast<TV>(static_cast<C>(f) * static_cast<C>(vec.w)),
+ };
}
using Vec4f = Vec4<float>;
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 @@
namespace Common {
-void* AllocateMemoryPages(std::size_t size) {
+void* AllocateMemoryPages(std::size_t size) noexcept {
#ifdef _WIN32
void* base{VirtualAlloc(nullptr, size, MEM_COMMIT, PAGE_READWRITE)};
#else
@@ -29,7 +29,7 @@ void* AllocateMemoryPages(std::size_t size) {
return base;
}
-void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) {
+void FreeMemoryPages(void* base, [[maybe_unused]] std::size_t size) noexcept {
if (!base) {
return;
}
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 @@
#pragma once
-#include "common/common_funcs.h"
+#include <type_traits>
+#include <utility>
namespace Common {
-void* AllocateMemoryPages(std::size_t size);
-void FreeMemoryPages(void* base, std::size_t size);
+void* AllocateMemoryPages(std::size_t size) noexcept;
+void FreeMemoryPages(void* base, std::size_t size) noexcept;
template <typename T>
-class VirtualBuffer final : NonCopyable {
+class VirtualBuffer final {
public:
+ static_assert(
+ std::is_trivially_constructible_v<T>,
+ "T must be trivially constructible, as non-trivial constructors will not be executed "
+ "with the current allocator");
+
constexpr VirtualBuffer() = default;
explicit VirtualBuffer(std::size_t count) : alloc_size{count * sizeof(T)} {
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}
- ~VirtualBuffer() {
+ ~VirtualBuffer() noexcept {
FreeMemoryPages(base_ptr, alloc_size);
}
+ VirtualBuffer(const VirtualBuffer&) = delete;
+ VirtualBuffer& operator=(const VirtualBuffer&) = delete;
+
+ VirtualBuffer(VirtualBuffer&& other) noexcept
+ : alloc_size{std::exchange(other.alloc_size, 0)}, base_ptr{std::exchange(other.base_ptr),
+ nullptr} {}
+
+ VirtualBuffer& operator=(VirtualBuffer&& other) noexcept {
+ alloc_size = std::exchange(other.alloc_size, 0);
+ base_ptr = std::exchange(other.base_ptr, nullptr);
+ return *this;
+ }
+
void resize(std::size_t count) {
+ const auto new_size = count * sizeof(T);
+ if (new_size == alloc_size) {
+ return;
+ }
+
FreeMemoryPages(base_ptr, alloc_size);
- alloc_size = count * sizeof(T);
+ alloc_size = new_size;
base_ptr = reinterpret_cast<T*>(AllocateMemoryPages(alloc_size));
}
diff --git a/src/common/wall_clock.cpp b/src/common/wall_clock.cpp
index 3afbdb898..452a2837e 100644
--- a/src/common/wall_clock.cpp
+++ b/src/common/wall_clock.cpp
@@ -15,7 +15,7 @@ namespace Common {
using base_timer = std::chrono::steady_clock;
using base_time_point = std::chrono::time_point<base_timer>;
-class StandardWallClock : public WallClock {
+class StandardWallClock final : public WallClock {
public:
StandardWallClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency)
: WallClock(emulated_cpu_frequency, emulated_clock_frequency, false) {
@@ -53,7 +53,7 @@ public:
return Common::Divide128On32(temporary, 1000000000).first;
}
- void Pause(bool is_paused) override {
+ void Pause([[maybe_unused]] bool is_paused) override {
// Do nothing in this clock type.
}
diff --git a/src/common/wall_clock.h b/src/common/wall_clock.h
index 5db30083d..bc7adfbf8 100644
--- a/src/common/wall_clock.h
+++ b/src/common/wall_clock.h
@@ -13,6 +13,8 @@ namespace Common {
class WallClock {
public:
+ virtual ~WallClock() = default;
+
/// Returns current wall time in nanoseconds
[[nodiscard]] virtual std::chrono::nanoseconds GetTimeNS() = 0;
diff --git a/src/common/x64/native_clock.h b/src/common/x64/native_clock.h
index 891a3bbfd..97aab6ac9 100644
--- a/src/common/x64/native_clock.h
+++ b/src/common/x64/native_clock.h
@@ -12,7 +12,7 @@
namespace Common {
namespace X64 {
-class NativeClock : public WallClock {
+class NativeClock final : public WallClock {
public:
NativeClock(u64 emulated_cpu_frequency, u64 emulated_clock_frequency, u64 rtsc_frequency);
@@ -34,7 +34,7 @@ private:
/// value used to reduce the native clocks accuracy as some apss rely on
/// undefined behavior where the level of accuracy in the clock shouldn't
/// be higher.
- static constexpr u64 inaccuracy_mask = ~(0x400 - 1);
+ static constexpr u64 inaccuracy_mask = ~(UINT64_C(0x400) - 1);
SpinLock rtsc_serialize{};
u64 last_measure{};
diff --git a/src/common/x64/xbyak_abi.h b/src/common/x64/xbyak_abi.h
index a5f5d4fc1..26e4bfda5 100644
--- a/src/common/x64/xbyak_abi.h
+++ b/src/common/x64/xbyak_abi.h
@@ -11,7 +11,7 @@
namespace Common::X64 {
-inline std::size_t RegToIndex(const Xbyak::Reg& reg) {
+constexpr std::size_t RegToIndex(const Xbyak::Reg& reg) {
using Kind = Xbyak::Reg::Kind;
ASSERT_MSG((reg.getKind() & (Kind::REG | Kind::XMM)) != 0,
"RegSet only support GPRs and XMM registers.");
@@ -19,17 +19,17 @@ inline std::size_t RegToIndex(const Xbyak::Reg& reg) {
return reg.getIdx() + (reg.getKind() == Kind::REG ? 0 : 16);
}
-inline Xbyak::Reg64 IndexToReg64(std::size_t reg_index) {
+constexpr Xbyak::Reg64 IndexToReg64(std::size_t reg_index) {
ASSERT(reg_index < 16);
return Xbyak::Reg64(static_cast<int>(reg_index));
}
-inline Xbyak::Xmm IndexToXmm(std::size_t reg_index) {
+constexpr Xbyak::Xmm IndexToXmm(std::size_t reg_index) {
ASSERT(reg_index >= 16 && reg_index < 32);
return Xbyak::Xmm(static_cast<int>(reg_index - 16));
}
-inline Xbyak::Reg IndexToReg(std::size_t reg_index) {
+constexpr Xbyak::Reg IndexToReg(std::size_t reg_index) {
if (reg_index < 16) {
return IndexToReg64(reg_index);
} else {
@@ -45,17 +45,17 @@ inline std::bitset<32> BuildRegSet(std::initializer_list<Xbyak::Reg> regs) {
return bits;
}
-const std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
-const std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
+constexpr inline std::bitset<32> ABI_ALL_GPRS(0x0000FFFF);
+constexpr inline std::bitset<32> ABI_ALL_XMMS(0xFFFF0000);
#ifdef _WIN32
// Microsoft x64 ABI
-const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
-const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
-const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
-const Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
-const Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
+constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
+constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rcx;
+constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rdx;
+constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::r8;
+constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::r9;
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
@@ -102,11 +102,11 @@ constexpr size_t ABI_SHADOW_SPACE = 0x20;
#else
// System V x86-64 ABI
-const Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
-const Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
-const Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
-const Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
-const Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
+constexpr inline Xbyak::Reg ABI_RETURN = Xbyak::util::rax;
+constexpr inline Xbyak::Reg ABI_PARAM1 = Xbyak::util::rdi;
+constexpr inline Xbyak::Reg ABI_PARAM2 = Xbyak::util::rsi;
+constexpr inline Xbyak::Reg ABI_PARAM3 = Xbyak::util::rdx;
+constexpr inline Xbyak::Reg ABI_PARAM4 = Xbyak::util::rcx;
const std::bitset<32> ABI_ALL_CALLER_SAVED = BuildRegSet({
// GPRs
diff --git a/src/core/CMakeLists.txt b/src/core/CMakeLists.txt
index c85c9485f..e370fd225 100644
--- a/src/core/CMakeLists.txt
+++ b/src/core/CMakeLists.txt
@@ -1,9 +1,3 @@
-if (YUZU_ENABLE_BOXCAT)
- set(BCAT_BOXCAT_ADDITIONAL_SOURCES hle/service/bcat/backend/boxcat.cpp hle/service/bcat/backend/boxcat.h)
-else()
- set(BCAT_BOXCAT_ADDITIONAL_SOURCES)
-endif()
-
add_library(core STATIC
arm/arm_interface.h
arm/arm_interface.cpp
@@ -19,8 +13,6 @@ add_library(core STATIC
arm/dynarmic/arm_exclusive_monitor.h
arm/exclusive_monitor.cpp
arm/exclusive_monitor.h
- arm/unicorn/arm_unicorn.cpp
- arm/unicorn/arm_unicorn.h
constants.cpp
constants.h
core.cpp
@@ -126,6 +118,8 @@ add_library(core STATIC
file_sys/vfs_vector.h
file_sys/xts_archive.cpp
file_sys/xts_archive.h
+ frontend/applets/controller.cpp
+ frontend/applets/controller.h
frontend/applets/error.cpp
frontend/applets/error.h
frontend/applets/general_frontend.cpp
@@ -244,6 +238,8 @@ add_library(core STATIC
hle/service/am/applet_oe.h
hle/service/am/applets/applets.cpp
hle/service/am/applets/applets.h
+ hle/service/am/applets/controller.cpp
+ hle/service/am/applets/controller.h
hle/service/am/applets/error.cpp
hle/service/am/applets/error.h
hle/service/am/applets/general_backend.cpp
@@ -299,7 +295,6 @@ add_library(core STATIC
hle/service/audio/hwopus.h
hle/service/bcat/backend/backend.cpp
hle/service/bcat/backend/backend.h
- ${BCAT_BOXCAT_ADDITIONAL_SOURCES}
hle/service/bcat/bcat.cpp
hle/service/bcat/bcat.h
hle/service/bcat/module.cpp
@@ -442,6 +437,8 @@ add_library(core STATIC
hle/service/nvdrv/devices/nvhost_gpu.h
hle/service/nvdrv/devices/nvhost_nvdec.cpp
hle/service/nvdrv/devices/nvhost_nvdec.h
+ hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
+ hle/service/nvdrv/devices/nvhost_nvdec_common.h
hle/service/nvdrv/devices/nvhost_nvjpg.cpp
hle/service/nvdrv/devices/nvhost_nvjpg.h
hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -455,10 +452,14 @@ add_library(core STATIC
hle/service/nvdrv/nvdrv.h
hle/service/nvdrv/nvmemp.cpp
hle/service/nvdrv/nvmemp.h
+ hle/service/nvdrv/syncpoint_manager.cpp
+ hle/service/nvdrv/syncpoint_manager.h
hle/service/nvflinger/buffer_queue.cpp
hle/service/nvflinger/buffer_queue.h
hle/service/nvflinger/nvflinger.cpp
hle/service/nvflinger/nvflinger.h
+ hle/service/olsc/olsc.cpp
+ hle/service/olsc/olsc.h
hle/service/pcie/pcie.cpp
hle/service/pcie/pcie.h
hle/service/pctl/module.cpp
@@ -491,6 +492,7 @@ add_library(core STATIC
hle/service/sm/controller.h
hle/service/sm/sm.cpp
hle/service/sm/sm.h
+ hle/service/sockets/blocking_worker.h
hle/service/sockets/bsd.cpp
hle/service/sockets/bsd.h
hle/service/sockets/ethc.cpp
@@ -501,6 +503,8 @@ add_library(core STATIC
hle/service/sockets/sfdnsres.h
hle/service/sockets/sockets.cpp
hle/service/sockets/sockets.h
+ hle/service/sockets/sockets_translate.cpp
+ hle/service/sockets/sockets_translate.h
hle/service/spl/csrng.cpp
hle/service/spl/csrng.h
hle/service/spl/module.cpp
@@ -601,6 +605,13 @@ add_library(core STATIC
tools/freezer.h
)
+if (YUZU_ENABLE_BOXCAT)
+ target_sources(core PRIVATE
+ hle/service/bcat/backend/boxcat.cpp
+ hle/service/bcat/backend/boxcat.h
+ )
+endif()
+
if (MSVC)
target_compile_options(core PRIVATE
# 'expression' : signed/unsigned mismatch
@@ -616,12 +627,26 @@ if (MSVC)
# 'context' : truncation from 'type1' to 'type2'
/we4305
)
+else()
+ target_compile_options(core PRIVATE
+ -Werror=conversion
+ -Werror=ignored-qualifiers
+ -Werror=implicit-fallthrough
+ -Werror=reorder
+ -Werror=sign-compare
+ -Werror=unused-variable
+
+ $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
+ $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
+
+ -Wno-sign-conversion
+ )
endif()
create_target_directory_groups(core)
target_link_libraries(core PUBLIC common PRIVATE audio_core video_core)
-target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus unicorn zip)
+target_link_libraries(core PUBLIC Boost::boost PRIVATE fmt::fmt nlohmann_json::nlohmann_json mbedtls opus zip)
if (YUZU_ENABLE_BOXCAT)
target_compile_definitions(core PRIVATE -DYUZU_ENABLE_BOXCAT)
diff --git a/src/core/arm/arm_interface.cpp b/src/core/arm/arm_interface.cpp
index d2295ed90..0951e1976 100644
--- a/src/core/arm/arm_interface.cpp
+++ b/src/core/arm/arm_interface.cpp
@@ -147,10 +147,18 @@ std::vector<ARM_Interface::BacktraceEntry> ARM_Interface::GetBacktraceFromContex
auto fp = ctx.cpu_registers[29];
auto lr = ctx.cpu_registers[30];
while (true) {
- out.push_back({"", 0, lr, 0});
- if (!fp) {
+ out.push_back({
+ .module = "",
+ .address = 0,
+ .original_address = lr,
+ .offset = 0,
+ .name = {},
+ });
+
+ if (fp == 0) {
break;
}
+
lr = memory.Read64(fp + 8) - 4;
fp = memory.Read64(fp);
}
diff --git a/src/core/arm/cpu_interrupt_handler.cpp b/src/core/arm/cpu_interrupt_handler.cpp
index df0350881..9c8898700 100644
--- a/src/core/arm/cpu_interrupt_handler.cpp
+++ b/src/core/arm/cpu_interrupt_handler.cpp
@@ -7,9 +7,7 @@
namespace Core {
-CPUInterruptHandler::CPUInterruptHandler() : is_interrupted{} {
- interrupt_event = std::make_unique<Common::Event>();
-}
+CPUInterruptHandler::CPUInterruptHandler() : interrupt_event{std::make_unique<Common::Event>()} {}
CPUInterruptHandler::~CPUInterruptHandler() = default;
@@ -17,7 +15,7 @@ void CPUInterruptHandler::SetInterrupt(bool is_interrupted_) {
if (is_interrupted_) {
interrupt_event->Set();
}
- this->is_interrupted = is_interrupted_;
+ is_interrupted = is_interrupted_;
}
void CPUInterruptHandler::AwaitInterrupt() {
diff --git a/src/core/arm/cpu_interrupt_handler.h b/src/core/arm/cpu_interrupt_handler.h
index 3d062d326..c20c280f1 100644
--- a/src/core/arm/cpu_interrupt_handler.h
+++ b/src/core/arm/cpu_interrupt_handler.h
@@ -4,6 +4,7 @@
#pragma once
+#include <atomic>
#include <memory>
namespace Common {
@@ -20,8 +21,8 @@ public:
CPUInterruptHandler(const CPUInterruptHandler&) = delete;
CPUInterruptHandler& operator=(const CPUInterruptHandler&) = delete;
- CPUInterruptHandler(CPUInterruptHandler&&) = default;
- CPUInterruptHandler& operator=(CPUInterruptHandler&&) = default;
+ CPUInterruptHandler(CPUInterruptHandler&&) = delete;
+ CPUInterruptHandler& operator=(CPUInterruptHandler&&) = delete;
bool IsInterrupted() const {
return is_interrupted;
@@ -32,8 +33,8 @@ public:
void AwaitInterrupt();
private:
- bool is_interrupted{};
std::unique_ptr<Common::Event> interrupt_event;
+ std::atomic_bool is_interrupted{false};
};
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_32.cpp b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
index b5f28a86e..6dc03f3b1 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_32.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_32.cpp
@@ -7,6 +7,7 @@
#include <dynarmic/A32/a32.h>
#include <dynarmic/A32/config.h>
#include <dynarmic/A32/context.h>
+#include "common/assert.h"
#include "common/logging/log.h"
#include "common/page_table.h"
#include "core/arm/cpu_interrupt_handler.h"
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.cpp b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
index ce9968724..9f170a224 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.cpp
@@ -6,6 +6,7 @@
#include <memory>
#include <dynarmic/A64/a64.h>
#include <dynarmic/A64/config.h>
+#include "common/assert.h"
#include "common/logging/log.h"
#include "common/page_table.h"
#include "core/arm/cpu_interrupt_handler.h"
@@ -13,7 +14,6 @@
#include "core/arm/dynarmic/arm_exclusive_monitor.h"
#include "core/core.h"
#include "core/core_timing.h"
-#include "core/core_timing_util.h"
#include "core/gdbstub/gdbstub.h"
#include "core/hardware_properties.h"
#include "core/hle/kernel/process.h"
@@ -82,16 +82,9 @@ public:
}
void InterpreterFallback(u64 pc, std::size_t num_instructions) override {
- LOG_INFO(Core_ARM, "Unicorn fallback @ 0x{:X} for {} instructions (instr = {:08X})", pc,
- num_instructions, MemoryReadCode(pc));
-
- ARM_Interface::ThreadContext64 ctx;
- parent.SaveContext(ctx);
- parent.inner_unicorn.LoadContext(ctx);
- parent.inner_unicorn.ExecuteInstructions(num_instructions);
- parent.inner_unicorn.SaveContext(ctx);
- parent.LoadContext(ctx);
- num_interpreted_instructions += num_instructions;
+ LOG_ERROR(Core_ARM,
+ "Unimplemented instruction @ 0x{:X} for {} instructions (instr = {:08X})", pc,
+ num_instructions, MemoryReadCode(pc));
}
void ExceptionRaised(u64 pc, Dynarmic::A64::Exception exception) override {
@@ -127,18 +120,17 @@ public:
if (parent.uses_wall_clock) {
return;
}
+
// Divide the number of ticks by the amount of CPU cores. TODO(Subv): This yields only a
// rough approximation of the amount of executed ticks in the system, it may be thrown off
// if not all cores are doing a similar amount of work. Instead of doing this, we should
// device a way so that timing is consistent across all cores without increasing the ticks 4
// times.
- u64 amortized_ticks =
- (ticks - num_interpreted_instructions) / Core::Hardware::NUM_CPU_CORES;
+ u64 amortized_ticks = ticks / Core::Hardware::NUM_CPU_CORES;
// Always execute at least one tick.
amortized_ticks = std::max<u64>(amortized_ticks, 1);
parent.system.CoreTiming().AddTicks(amortized_ticks);
- num_interpreted_instructions = 0;
}
u64 GetTicksRemaining() override {
@@ -156,7 +148,6 @@ public:
}
ARM_Dynarmic_64& parent;
- std::size_t num_interpreted_instructions = 0;
u64 tpidrro_el0 = 0;
u64 tpidr_el0 = 0;
static constexpr u64 minimum_run_cycles = 1000U;
@@ -248,12 +239,8 @@ ARM_Dynarmic_64::ARM_Dynarmic_64(System& system, CPUInterrupts& interrupt_handle
bool uses_wall_clock, ExclusiveMonitor& exclusive_monitor,
std::size_t core_index)
: ARM_Interface{system, interrupt_handlers, uses_wall_clock},
- cb(std::make_unique<DynarmicCallbacks64>(*this)), inner_unicorn{system, interrupt_handlers,
- uses_wall_clock,
- ARM_Unicorn::Arch::AArch64,
- core_index},
- core_index{core_index}, exclusive_monitor{
- dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
+ cb(std::make_unique<DynarmicCallbacks64>(*this)), core_index{core_index},
+ exclusive_monitor{dynamic_cast<DynarmicExclusiveMonitor&>(exclusive_monitor)} {}
ARM_Dynarmic_64::~ARM_Dynarmic_64() = default;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_64.h b/src/core/arm/dynarmic/arm_dynarmic_64.h
index 403c55961..28e11a17d 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_64.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_64.h
@@ -12,7 +12,6 @@
#include "common/hash.h"
#include "core/arm/arm_interface.h"
#include "core/arm/exclusive_monitor.h"
-#include "core/arm/unicorn/arm_unicorn.h"
namespace Core::Memory {
class Memory;
@@ -71,7 +70,6 @@ private:
std::unique_ptr<DynarmicCallbacks64> cb;
JitCacheType jit_cache;
std::shared_ptr<Dynarmic::A64::Jit> jit;
- ARM_Unicorn inner_unicorn;
std::size_t core_index;
DynarmicExclusiveMonitor& exclusive_monitor;
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
index 54556e0f9..caefc09f4 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.cpp
@@ -34,7 +34,7 @@ std::optional<Callback> DynarmicCP15::CompileInternalOperation(bool two, unsigne
CoprocReg CRm, unsigned opc2) {
LOG_CRITICAL(Core_ARM, "CP15: cdp{} p15, {}, {}, {}, {}, {}", two ? "2" : "", opc1, CRd, CRn,
CRm, opc2);
- return {};
+ return std::nullopt;
}
CallbackOrAccessOneWord DynarmicCP15::CompileSendOneWord(bool two, unsigned opc1, CoprocReg CRn,
@@ -115,7 +115,7 @@ std::optional<Callback> DynarmicCP15::CompileLoadWords(bool two, bool long_trans
LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
long_transfer ? "l" : "", CRd);
}
- return {};
+ return std::nullopt;
}
std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_transfer, CoprocReg CRd,
@@ -127,7 +127,7 @@ std::optional<Callback> DynarmicCP15::CompileStoreWords(bool two, bool long_tran
LOG_CRITICAL(Core_ARM, "CP15: mrrc{}{} p15, {}, [...]", two ? "2" : "",
long_transfer ? "l" : "", CRd);
}
- return {};
+ return std::nullopt;
}
} // namespace Core
diff --git a/src/core/arm/dynarmic/arm_dynarmic_cp15.h b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
index 7356d252e..dc6f4af3a 100644
--- a/src/core/arm/dynarmic/arm_dynarmic_cp15.h
+++ b/src/core/arm/dynarmic/arm_dynarmic_cp15.h
@@ -35,8 +35,8 @@ public:
std::optional<u8> option) override;
ARM_Dynarmic_32& parent;
- u32 uprw;
- u32 uro;
+ u32 uprw = 0;
+ u32 uro = 0;
};
} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.cpp b/src/core/arm/unicorn/arm_unicorn.cpp
deleted file mode 100644
index 1df3f3ed1..000000000
--- a/src/core/arm/unicorn/arm_unicorn.cpp
+++ /dev/null
@@ -1,295 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <unicorn/arm64.h>
-#include "common/assert.h"
-#include "common/microprofile.h"
-#include "core/arm/cpu_interrupt_handler.h"
-#include "core/arm/unicorn/arm_unicorn.h"
-#include "core/core.h"
-#include "core/core_timing.h"
-#include "core/hle/kernel/scheduler.h"
-#include "core/hle/kernel/svc.h"
-#include "core/memory.h"
-
-namespace Core {
-
-// Load Unicorn DLL once on Windows using RAII
-#ifdef _MSC_VER
-#include <unicorn_dynload.h>
-struct LoadDll {
-private:
- LoadDll() {
- ASSERT(uc_dyn_load(NULL, 0));
- }
- ~LoadDll() {
- ASSERT(uc_dyn_free());
- }
- static LoadDll g_load_dll;
-};
-LoadDll LoadDll::g_load_dll;
-#endif
-
-#define CHECKED(expr) \
- do { \
- if (auto _cerr = (expr)) { \
- ASSERT_MSG(false, "Call " #expr " failed with error: {} ({})\n", _cerr, \
- uc_strerror(_cerr)); \
- } \
- } while (0)
-
-static void CodeHook(uc_engine* uc, uint64_t address, uint32_t size, void* user_data) {
- GDBStub::BreakpointAddress bkpt =
- GDBStub::GetNextBreakpointFromAddress(address, GDBStub::BreakpointType::Execute);
- if (GDBStub::IsMemoryBreak() ||
- (bkpt.type != GDBStub::BreakpointType::None && address == bkpt.address)) {
- auto core = static_cast<ARM_Unicorn*>(user_data);
- core->RecordBreak(bkpt);
- uc_emu_stop(uc);
- }
-}
-
-static bool UnmappedMemoryHook(uc_engine* uc, uc_mem_type type, u64 addr, int size, u64 value,
- void* user_data) {
- auto* const system = static_cast<System*>(user_data);
-
- ARM_Interface::ThreadContext64 ctx{};
- system->CurrentArmInterface().SaveContext(ctx);
- ASSERT_MSG(false, "Attempted to read from unmapped memory: 0x{:X}, pc=0x{:X}, lr=0x{:X}", addr,
- ctx.pc, ctx.cpu_registers[30]);
-
- return false;
-}
-
-ARM_Unicorn::ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
- Arch architecture, std::size_t core_index)
- : ARM_Interface{system, interrupt_handlers, uses_wall_clock}, core_index{core_index} {
- const auto arch = architecture == Arch::AArch32 ? UC_ARCH_ARM : UC_ARCH_ARM64;
- CHECKED(uc_open(arch, UC_MODE_ARM, &uc));
-
- auto fpv = 3 << 20;
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_CPACR_EL1, &fpv));
-
- uc_hook hook{};
- CHECKED(uc_hook_add(uc, &hook, UC_HOOK_INTR, (void*)InterruptHook, this, 0, UINT64_MAX));
- CHECKED(uc_hook_add(uc, &hook, UC_HOOK_MEM_INVALID, (void*)UnmappedMemoryHook, &system, 0,
- UINT64_MAX));
- if (GDBStub::IsServerEnabled()) {
- CHECKED(uc_hook_add(uc, &hook, UC_HOOK_CODE, (void*)CodeHook, this, 0, UINT64_MAX));
- last_bkpt_hit = false;
- }
-}
-
-ARM_Unicorn::~ARM_Unicorn() {
- CHECKED(uc_close(uc));
-}
-
-void ARM_Unicorn::SetPC(u64 pc) {
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &pc));
-}
-
-u64 ARM_Unicorn::GetPC() const {
- u64 val{};
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &val));
- return val;
-}
-
-u64 ARM_Unicorn::GetReg(int regn) const {
- u64 val{};
- auto treg = UC_ARM64_REG_SP;
- if (regn <= 28) {
- treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
- } else if (regn < 31) {
- treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
- }
- CHECKED(uc_reg_read(uc, treg, &val));
- return val;
-}
-
-void ARM_Unicorn::SetReg(int regn, u64 val) {
- auto treg = UC_ARM64_REG_SP;
- if (regn <= 28) {
- treg = (uc_arm64_reg)(UC_ARM64_REG_X0 + regn);
- } else if (regn < 31) {
- treg = (uc_arm64_reg)(UC_ARM64_REG_X29 + regn - 29);
- }
- CHECKED(uc_reg_write(uc, treg, &val));
-}
-
-u128 ARM_Unicorn::GetVectorReg(int /*index*/) const {
- UNIMPLEMENTED();
- static constexpr u128 res{};
- return res;
-}
-
-void ARM_Unicorn::SetVectorReg(int /*index*/, u128 /*value*/) {
- UNIMPLEMENTED();
-}
-
-u32 ARM_Unicorn::GetPSTATE() const {
- u64 nzcv{};
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &nzcv));
- return static_cast<u32>(nzcv);
-}
-
-void ARM_Unicorn::SetPSTATE(u32 pstate) {
- u64 nzcv = pstate;
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &nzcv));
-}
-
-VAddr ARM_Unicorn::GetTlsAddress() const {
- u64 base{};
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
- return base;
-}
-
-void ARM_Unicorn::SetTlsAddress(VAddr base) {
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDRRO_EL0, &base));
-}
-
-u64 ARM_Unicorn::GetTPIDR_EL0() const {
- u64 value{};
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_TPIDR_EL0, &value));
- return value;
-}
-
-void ARM_Unicorn::SetTPIDR_EL0(u64 value) {
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_TPIDR_EL0, &value));
-}
-
-void ARM_Unicorn::ChangeProcessorID(std::size_t new_core_id) {
- core_index = new_core_id;
-}
-
-void ARM_Unicorn::Run() {
- if (GDBStub::IsServerEnabled()) {
- ExecuteInstructions(std::max(4000000U, 0U));
- } else {
- while (true) {
- if (interrupt_handlers[core_index].IsInterrupted()) {
- return;
- }
- ExecuteInstructions(10);
- }
- }
-}
-
-void ARM_Unicorn::Step() {
- ExecuteInstructions(1);
-}
-
-MICROPROFILE_DEFINE(ARM_Jit_Unicorn, "ARM JIT", "Unicorn", MP_RGB(255, 64, 64));
-
-void ARM_Unicorn::ExecuteInstructions(std::size_t num_instructions) {
- MICROPROFILE_SCOPE(ARM_Jit_Unicorn);
-
- // Temporarily map the code page for Unicorn
- u64 map_addr{GetPC() & ~Memory::PAGE_MASK};
- std::vector<u8> page_buffer(Memory::PAGE_SIZE);
- system.Memory().ReadBlock(map_addr, page_buffer.data(), page_buffer.size());
-
- CHECKED(uc_mem_map_ptr(uc, map_addr, page_buffer.size(),
- UC_PROT_READ | UC_PROT_WRITE | UC_PROT_EXEC, page_buffer.data()));
- CHECKED(uc_emu_start(uc, GetPC(), 1ULL << 63, 0, num_instructions));
- CHECKED(uc_mem_unmap(uc, map_addr, page_buffer.size()));
- if (GDBStub::IsServerEnabled()) {
- if (last_bkpt_hit && last_bkpt.type == GDBStub::BreakpointType::Execute) {
- uc_reg_write(uc, UC_ARM64_REG_PC, &last_bkpt.address);
- }
-
- Kernel::Thread* const thread = system.CurrentScheduler().GetCurrentThread();
- SaveContext(thread->GetContext64());
- if (last_bkpt_hit || GDBStub::IsMemoryBreak() || GDBStub::GetCpuStepFlag()) {
- last_bkpt_hit = false;
- GDBStub::Break();
- GDBStub::SendTrap(thread, 5);
- }
- }
-}
-
-void ARM_Unicorn::SaveContext(ThreadContext64& ctx) {
- int uregs[32];
- void* tregs[32];
-
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_SP, &ctx.sp));
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_PC, &ctx.pc));
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
-
- for (auto i = 0; i < 29; ++i) {
- uregs[i] = UC_ARM64_REG_X0 + i;
- tregs[i] = &ctx.cpu_registers[i];
- }
- uregs[29] = UC_ARM64_REG_X29;
- tregs[29] = (void*)&ctx.cpu_registers[29];
- uregs[30] = UC_ARM64_REG_X30;
- tregs[30] = (void*)&ctx.cpu_registers[30];
-
- CHECKED(uc_reg_read_batch(uc, uregs, tregs, 31));
-
- for (int i = 0; i < 32; ++i) {
- uregs[i] = UC_ARM64_REG_Q0 + i;
- tregs[i] = &ctx.vector_registers[i];
- }
-
- CHECKED(uc_reg_read_batch(uc, uregs, tregs, 32));
-}
-
-void ARM_Unicorn::LoadContext(const ThreadContext64& ctx) {
- int uregs[32];
- void* tregs[32];
-
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_SP, &ctx.sp));
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_PC, &ctx.pc));
- CHECKED(uc_reg_write(uc, UC_ARM64_REG_NZCV, &ctx.pstate));
-
- for (int i = 0; i < 29; ++i) {
- uregs[i] = UC_ARM64_REG_X0 + i;
- tregs[i] = (void*)&ctx.cpu_registers[i];
- }
- uregs[29] = UC_ARM64_REG_X29;
- tregs[29] = (void*)&ctx.cpu_registers[29];
- uregs[30] = UC_ARM64_REG_X30;
- tregs[30] = (void*)&ctx.cpu_registers[30];
-
- CHECKED(uc_reg_write_batch(uc, uregs, tregs, 31));
-
- for (auto i = 0; i < 32; ++i) {
- uregs[i] = UC_ARM64_REG_Q0 + i;
- tregs[i] = (void*)&ctx.vector_registers[i];
- }
-
- CHECKED(uc_reg_write_batch(uc, uregs, tregs, 32));
-}
-
-void ARM_Unicorn::PrepareReschedule() {
- CHECKED(uc_emu_stop(uc));
-}
-
-void ARM_Unicorn::ClearExclusiveState() {}
-
-void ARM_Unicorn::ClearInstructionCache() {}
-
-void ARM_Unicorn::RecordBreak(GDBStub::BreakpointAddress bkpt) {
- last_bkpt = bkpt;
- last_bkpt_hit = true;
-}
-
-void ARM_Unicorn::InterruptHook(uc_engine* uc, u32 int_no, void* user_data) {
- u32 esr{};
- CHECKED(uc_reg_read(uc, UC_ARM64_REG_ESR, &esr));
-
- const auto ec = esr >> 26;
- const auto iss = esr & 0xFFFFFF;
-
- auto* const arm_instance = static_cast<ARM_Unicorn*>(user_data);
-
- switch (ec) {
- case 0x15: // SVC
- Kernel::Svc::Call(arm_instance->system, iss);
- break;
- }
-}
-
-} // namespace Core
diff --git a/src/core/arm/unicorn/arm_unicorn.h b/src/core/arm/unicorn/arm_unicorn.h
deleted file mode 100644
index 810aff311..000000000
--- a/src/core/arm/unicorn/arm_unicorn.h
+++ /dev/null
@@ -1,63 +0,0 @@
-// Copyright 2018 yuzu emulator team
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <unicorn/unicorn.h>
-#include "common/common_types.h"
-#include "core/arm/arm_interface.h"
-#include "core/gdbstub/gdbstub.h"
-
-namespace Core {
-
-class System;
-
-class ARM_Unicorn final : public ARM_Interface {
-public:
- enum class Arch {
- AArch32, // 32-bit ARM
- AArch64, // 64-bit ARM
- };
-
- explicit ARM_Unicorn(System& system, CPUInterrupts& interrupt_handlers, bool uses_wall_clock,
- Arch architecture, std::size_t core_index);
- ~ARM_Unicorn() override;
-
- void SetPC(u64 pc) override;
- u64 GetPC() const override;
- u64 GetReg(int index) const override;
- void SetReg(int index, u64 value) override;
- u128 GetVectorReg(int index) const override;
- void SetVectorReg(int index, u128 value) override;
- u32 GetPSTATE() const override;
- void SetPSTATE(u32 pstate) override;
- VAddr GetTlsAddress() const override;
- void SetTlsAddress(VAddr address) override;
- void SetTPIDR_EL0(u64 value) override;
- u64 GetTPIDR_EL0() const override;
- void ChangeProcessorID(std::size_t new_core_id) override;
- void PrepareReschedule() override;
- void ClearExclusiveState() override;
- void ExecuteInstructions(std::size_t num_instructions);
- void Run() override;
- void Step() override;
- void ClearInstructionCache() override;
- void PageTableChanged(Common::PageTable&, std::size_t) override {}
- void RecordBreak(GDBStub::BreakpointAddress bkpt);
-
- void SaveContext(ThreadContext32& ctx) override {}
- void SaveContext(ThreadContext64& ctx) override;
- void LoadContext(const ThreadContext32& ctx) override {}
- void LoadContext(const ThreadContext64& ctx) override;
-
-private:
- static void InterruptHook(uc_engine* uc, u32 int_no, void* user_data);
-
- uc_engine* uc{};
- GDBStub::BreakpointAddress last_bkpt{};
- bool last_bkpt_hit = false;
- std::size_t core_index;
-};
-
-} // namespace Core
diff --git a/src/core/core.cpp b/src/core/core.cpp
index c2c0eec0b..9253e05b7 100644
--- a/src/core/core.cpp
+++ b/src/core/core.cpp
@@ -40,6 +40,7 @@
#include "core/hle/service/lm/manager.h"
#include "core/hle/service/service.h"
#include "core/hle/service/sm/sm.h"
+#include "core/hle/service/time/time_manager.h"
#include "core/loader/loader.h"
#include "core/memory.h"
#include "core/memory/cheat_engine.h"
@@ -121,7 +122,7 @@ FileSys::VirtualFile GetGameFileFromPath(const FileSys::VirtualFilesystem& vfs,
struct System::Impl {
explicit Impl(System& system)
: kernel{system}, fs_controller{system}, memory{system},
- cpu_manager{system}, reporter{system}, applet_manager{system} {}
+ cpu_manager{system}, reporter{system}, applet_manager{system}, time_manager{system} {}
ResultStatus Run() {
status = ResultStatus::Success;
@@ -178,17 +179,21 @@ struct System::Impl {
arp_manager.ResetAll();
telemetry_session = std::make_unique<Core::TelemetrySession>();
- service_manager = std::make_shared<Service::SM::ServiceManager>();
- Service::Init(service_manager, system);
- GDBStub::DeferStart();
-
- interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
gpu_core = VideoCore::CreateGPU(emu_window, system);
if (!gpu_core) {
return ResultStatus::ErrorVideoCore;
}
- gpu_core->Renderer().Rasterizer().SetupDirtyFlags();
+
+ service_manager = std::make_shared<Service::SM::ServiceManager>(kernel);
+
+ Service::Init(service_manager, system);
+ GDBStub::DeferStart();
+
+ interrupt_manager = std::make_unique<Core::Hardware::InterruptManager>(system);
+
+ // Initialize time manager, which must happen after kernel is created
+ time_manager.Initialize();
is_powered_on = true;
exit_lock = false;
@@ -205,7 +210,7 @@ struct System::Impl {
ResultStatus Load(System& system, Frontend::EmuWindow& emu_window,
const std::string& filepath) {
- app_loader = Loader::GetLoader(GetGameFileFromPath(virtual_filesystem, filepath));
+ app_loader = Loader::GetLoader(system, GetGameFileFromPath(virtual_filesystem, filepath));
if (!app_loader) {
LOG_CRITICAL(Core, "Failed to obtain loader for {}!", filepath);
return ResultStatus::ErrorGetLoader;
@@ -219,10 +224,10 @@ struct System::Impl {
return init_result;
}
- telemetry_session->AddInitialInfo(*app_loader);
+ telemetry_session->AddInitialInfo(*app_loader, fs_controller, *content_provider);
auto main_process =
Kernel::Process::Create(system, "main", Kernel::Process::ProcessType::Userland);
- const auto [load_result, load_parameters] = app_loader->Load(*main_process);
+ const auto [load_result, load_parameters] = app_loader->Load(*main_process, system);
if (load_result != Loader::ResultStatus::Success) {
LOG_CRITICAL(Core, "Failed to load ROM (Error {})!", static_cast<int>(load_result));
Shutdown();
@@ -333,7 +338,7 @@ struct System::Impl {
Service::Glue::ApplicationLaunchProperty launch{};
launch.title_id = process.GetTitleID();
- FileSys::PatchManager pm{launch.title_id};
+ FileSys::PatchManager pm{launch.title_id, fs_controller, *content_provider};
launch.version = pm.GetGameVersion().value_or(0);
// TODO(DarkLordZach): When FSController/Game Card Support is added, if
@@ -388,6 +393,7 @@ struct System::Impl {
/// Service State
Service::Glue::ARPManager arp_manager;
Service::LM::Manager lm_manager{reporter};
+ Service::Time::TimeManager time_manager;
/// Service manager
std::shared_ptr<Service::SM::ServiceManager> service_manager;
@@ -630,11 +636,11 @@ Loader::AppLoader& System::GetAppLoader() const {
return *impl->app_loader;
}
-void System::SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs) {
+void System::SetFilesystem(FileSys::VirtualFilesystem vfs) {
impl->virtual_filesystem = std::move(vfs);
}
-std::shared_ptr<FileSys::VfsFilesystem> System::GetFilesystem() const {
+FileSys::VirtualFilesystem System::GetFilesystem() const {
return impl->virtual_filesystem;
}
@@ -718,6 +724,14 @@ const Service::LM::Manager& System::GetLogManager() const {
return impl->lm_manager;
}
+Service::Time::TimeManager& System::GetTimeManager() {
+ return impl->time_manager;
+}
+
+const Service::Time::TimeManager& System::GetTimeManager() const {
+ return impl->time_manager;
+}
+
void System::SetExitLock(bool locked) {
impl->exit_lock = locked;
}
diff --git a/src/core/core.h b/src/core/core.h
index 5c6cfbffe..6db896bae 100644
--- a/src/core/core.h
+++ b/src/core/core.h
@@ -69,6 +69,10 @@ namespace SM {
class ServiceManager;
} // namespace SM
+namespace Time {
+class TimeManager;
+} // namespace Time
+
} // namespace Service
namespace Tegra {
@@ -120,7 +124,7 @@ public:
* Gets the instance of the System singleton class.
* @returns Reference to the instance of the System singleton class.
*/
- static System& GetInstance() {
+ [[deprecated("Use of the global system instance is deprecated")]] static System& GetInstance() {
return s_instance;
}
@@ -316,9 +320,9 @@ public:
Service::SM::ServiceManager& ServiceManager();
const Service::SM::ServiceManager& ServiceManager() const;
- void SetFilesystem(std::shared_ptr<FileSys::VfsFilesystem> vfs);
+ void SetFilesystem(FileSys::VirtualFilesystem vfs);
- std::shared_ptr<FileSys::VfsFilesystem> GetFilesystem() const;
+ FileSys::VirtualFilesystem GetFilesystem() const;
void RegisterCheatList(const std::vector<Memory::CheatEntry>& list,
const std::array<u8, 0x20>& build_id, VAddr main_region_begin,
@@ -361,6 +365,10 @@ public:
const Service::LM::Manager& GetLogManager() const;
+ Service::Time::TimeManager& GetTimeManager();
+
+ const Service::Time::TimeManager& GetTimeManager() const;
+
void SetExitLock(bool locked);
bool GetExitLock() const;
diff --git a/src/core/cpu_manager.cpp b/src/core/cpu_manager.cpp
index ef0bae556..983210197 100644
--- a/src/core/cpu_manager.cpp
+++ b/src/core/cpu_manager.cpp
@@ -328,7 +328,7 @@ void CpuManager::RunThread(std::size_t core) {
system.RegisterCoreThread(core);
std::string name;
if (is_multicore) {
- name = "yuzu:CoreCPUThread_" + std::to_string(core);
+ name = "yuzu:CPUCore_" + std::to_string(core);
} else {
name = "yuzu:CPUThread";
}
@@ -365,6 +365,8 @@ void CpuManager::RunThread(std::size_t core) {
data.enter_barrier.reset();
data.exit_barrier.reset();
data.initialized = false;
+
+ MicroProfileOnThreadExit();
}
} // namespace Core
diff --git a/src/core/crypto/key_manager.cpp b/src/core/crypto/key_manager.cpp
index dc591c730..da15f764a 100644
--- a/src/core/crypto/key_manager.cpp
+++ b/src/core/crypto/key_manager.cpp
@@ -23,7 +23,6 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
-#include "core/core.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/key_manager.h"
#include "core/crypto/partition_data_manager.h"
@@ -412,7 +411,7 @@ Loader::ResultStatus DeriveSDKeys(std::array<Key256, 2>& sd_keys, KeyManager& ke
// Combine sources and seed
for (auto& source : sd_key_sources) {
for (std::size_t i = 0; i < source.size(); ++i) {
- source[i] ^= sd_seed[i & 0xF];
+ source[i] = static_cast<u8>(source[i] ^ sd_seed[i & 0xF]);
}
}
@@ -1022,10 +1021,10 @@ void KeyManager::DeriveBase() {
}
}
-void KeyManager::DeriveETicket(PartitionDataManager& data) {
+void KeyManager::DeriveETicket(PartitionDataManager& data,
+ const FileSys::ContentProvider& provider) {
// ETicket keys
- const auto es = Core::System::GetInstance().GetContentProvider().GetEntry(
- 0x0100000000000033, FileSys::ContentRecordType::Program);
+ const auto es = provider.GetEntry(0x0100000000000033, FileSys::ContentRecordType::Program);
if (es == nullptr) {
return;
diff --git a/src/core/crypto/key_manager.h b/src/core/crypto/key_manager.h
index 321b75323..0a7220286 100644
--- a/src/core/crypto/key_manager.h
+++ b/src/core/crypto/key_manager.h
@@ -20,6 +20,10 @@ namespace Common::FS {
class IOFile;
}
+namespace FileSys {
+class ContentProvider;
+}
+
namespace Loader {
enum class ResultStatus : u16;
}
@@ -252,7 +256,7 @@ public:
bool BaseDeriveNecessary() const;
void DeriveBase();
- void DeriveETicket(PartitionDataManager& data);
+ void DeriveETicket(PartitionDataManager& data, const FileSys::ContentProvider& provider);
void PopulateTickets();
void SynthesizeTickets();
diff --git a/src/core/crypto/partition_data_manager.cpp b/src/core/crypto/partition_data_manager.cpp
index 46136d04a..5f1c86a09 100644
--- a/src/core/crypto/partition_data_manager.cpp
+++ b/src/core/crypto/partition_data_manager.cpp
@@ -26,6 +26,7 @@
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/vfs_vector.h"
+#include "core/loader/loader.h"
using Common::AsArray;
diff --git a/src/core/file_sys/bis_factory.cpp b/src/core/file_sys/bis_factory.cpp
index 9ffda2e14..7c6304ff0 100644
--- a/src/core/file_sys/bis_factory.cpp
+++ b/src/core/file_sys/bis_factory.cpp
@@ -4,11 +4,10 @@
#include <fmt/format.h>
#include "common/file_util.h"
-#include "core/core.h"
#include "core/file_sys/bis_factory.h"
#include "core/file_sys/mode.h"
#include "core/file_sys/registered_cache.h"
-#include "core/settings.h"
+#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -82,11 +81,11 @@ VirtualDir BISFactory::OpenPartition(BisPartitionId id) const {
}
}
-VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id) const {
+VirtualFile BISFactory::OpenPartitionStorage(BisPartitionId id,
+ VirtualFilesystem file_system) const {
auto& keys = Core::Crypto::KeyManager::Instance();
- Core::Crypto::PartitionDataManager pdm{
- Core::System::GetInstance().GetFilesystem()->OpenDirectory(
- Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
+ Core::Crypto::PartitionDataManager pdm{file_system->OpenDirectory(
+ Common::FS::GetUserPath(Common::FS::UserPath::SysDataDir), Mode::Read)};
keys.PopulateFromPartitionData(pdm);
switch (id) {
diff --git a/src/core/file_sys/bis_factory.h b/src/core/file_sys/bis_factory.h
index 8f0451c98..136485881 100644
--- a/src/core/file_sys/bis_factory.h
+++ b/src/core/file_sys/bis_factory.h
@@ -6,7 +6,8 @@
#include <memory>
-#include "core/file_sys/vfs.h"
+#include "common/common_types.h"
+#include "core/file_sys/vfs_types.h"
namespace FileSys {
@@ -51,7 +52,7 @@ public:
VirtualDir GetModificationDumpRoot(u64 title_id) const;
VirtualDir OpenPartition(BisPartitionId id) const;
- VirtualFile OpenPartitionStorage(BisPartitionId id) const;
+ VirtualFile OpenPartitionStorage(BisPartitionId id, VirtualFilesystem file_system) const;
VirtualDir GetImageDirectory() const;
diff --git a/src/core/file_sys/card_image.cpp b/src/core/file_sys/card_image.cpp
index 664a47e7f..956da68f7 100644
--- a/src/core/file_sys/card_image.cpp
+++ b/src/core/file_sys/card_image.cpp
@@ -8,11 +8,11 @@
#include <fmt/ostream.h>
#include "common/logging/log.h"
+#include "core/crypto/key_manager.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
#include "core/file_sys/partition_filesystem.h"
-#include "core/file_sys/romfs.h"
#include "core/file_sys/submission_package.h"
#include "core/file_sys/vfs_concat.h"
#include "core/file_sys/vfs_offset.h"
@@ -31,7 +31,8 @@ constexpr std::array partition_names{
XCI::XCI(VirtualFile file_)
: file(std::move(file_)), program_nca_status{Loader::ResultStatus::ErrorXCIMissingProgramNCA},
- partitions(partition_names.size()), partitions_raw(partition_names.size()) {
+ partitions(partition_names.size()),
+ partitions_raw(partition_names.size()), keys{Core::Crypto::KeyManager::Instance()} {
if (file->ReadObject(&header) != sizeof(GamecardHeader)) {
status = Loader::ResultStatus::ErrorBadXCIHeader;
return;
diff --git a/src/core/file_sys/card_image.h b/src/core/file_sys/card_image.h
index e1b136426..2d0a0f285 100644
--- a/src/core/file_sys/card_image.h
+++ b/src/core/file_sys/card_image.h
@@ -9,9 +9,12 @@
#include <vector>
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/crypto/key_manager.h"
#include "core/file_sys/vfs.h"
+namespace Core::Crypto {
+class KeyManager;
+}
+
namespace Loader {
enum class ResultStatus : u16;
}
@@ -140,6 +143,6 @@ private:
u64 update_normal_partition_end;
- Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
+ Core::Crypto::KeyManager& keys;
};
} // namespace FileSys
diff --git a/src/core/file_sys/content_archive.cpp b/src/core/file_sys/content_archive.cpp
index 5039341c7..76af47ff9 100644
--- a/src/core/file_sys/content_archive.cpp
+++ b/src/core/file_sys/content_archive.cpp
@@ -10,10 +10,10 @@
#include "common/logging/log.h"
#include "core/crypto/aes_util.h"
#include "core/crypto/ctr_encryption_layer.h"
+#include "core/crypto/key_manager.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_patch.h"
#include "core/file_sys/partition_filesystem.h"
-#include "core/file_sys/romfs.h"
#include "core/file_sys/vfs_offset.h"
#include "core/loader/loader.h"
@@ -119,7 +119,8 @@ static bool IsValidNCA(const NCAHeader& header) {
}
NCA::NCA(VirtualFile file_, VirtualFile bktr_base_romfs_, u64 bktr_base_ivfc_offset)
- : file(std::move(file_)), bktr_base_romfs(std::move(bktr_base_romfs_)) {
+ : file(std::move(file_)),
+ bktr_base_romfs(std::move(bktr_base_romfs_)), keys{Core::Crypto::KeyManager::Instance()} {
if (file == nullptr) {
status = Loader::ResultStatus::ErrorNullFile;
return;
@@ -322,7 +323,7 @@ bool NCA::ReadRomFSSection(const NCASectionHeader& section, const NCASectionTabl
subsection_buckets.back().entries.push_back({section.bktr.relocation.offset, {0}, ctr_low});
subsection_buckets.back().entries.push_back({size, {0}, 0});
- std::optional<Core::Crypto::Key128> key = {};
+ std::optional<Core::Crypto::Key128> key;
if (encrypted) {
if (has_rights_id) {
status = Loader::ResultStatus::Success;
@@ -441,18 +442,18 @@ std::optional<Core::Crypto::Key128> NCA::GetTitlekey() {
memcpy(rights_id.data(), header.rights_id.data(), 16);
if (rights_id == u128{}) {
status = Loader::ResultStatus::ErrorInvalidRightsID;
- return {};
+ return std::nullopt;
}
auto titlekey = keys.GetKey(Core::Crypto::S128KeyType::Titlekey, rights_id[1], rights_id[0]);
if (titlekey == Core::Crypto::Key128{}) {
status = Loader::ResultStatus::ErrorMissingTitlekey;
- return {};
+ return std::nullopt;
}
if (!keys.HasKey(Core::Crypto::S128KeyType::Titlekek, master_key_id)) {
status = Loader::ResultStatus::ErrorMissingTitlekek;
- return {};
+ return std::nullopt;
}
Core::Crypto::AESCipher<Core::Crypto::Key128> cipher(
@@ -476,7 +477,7 @@ VirtualFile NCA::Decrypt(const NCASectionHeader& s_header, VirtualFile in, u64 s
case NCASectionCryptoType::BKTR:
LOG_TRACE(Crypto, "called with mode=CTR, starting_offset={:016X}", starting_offset);
{
- std::optional<Core::Crypto::Key128> key = {};
+ std::optional<Core::Crypto::Key128> key;
if (has_rights_id) {
status = Loader::ResultStatus::Success;
key = GetTitlekey();
diff --git a/src/core/file_sys/content_archive.h b/src/core/file_sys/content_archive.h
index d25cbcf91..69292232a 100644
--- a/src/core/file_sys/content_archive.h
+++ b/src/core/file_sys/content_archive.h
@@ -158,7 +158,7 @@ private:
bool encrypted = false;
bool is_update = false;
- Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
+ Core::Crypto::KeyManager& keys;
};
} // namespace FileSys
diff --git a/src/core/file_sys/control_metadata.cpp b/src/core/file_sys/control_metadata.cpp
index 63cd2eead..b0a130345 100644
--- a/src/core/file_sys/control_metadata.cpp
+++ b/src/core/file_sys/control_metadata.cpp
@@ -5,6 +5,7 @@
#include "common/string_util.h"
#include "common/swap.h"
#include "core/file_sys/control_metadata.h"
+#include "core/file_sys/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/control_metadata.h b/src/core/file_sys/control_metadata.h
index e37b2fadf..403c4219a 100644
--- a/src/core/file_sys/control_metadata.h
+++ b/src/core/file_sys/control_metadata.h
@@ -10,7 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_types.h"
namespace FileSys {
@@ -83,7 +83,7 @@ enum class Language : u8 {
Italian = 7,
Dutch = 8,
CanadianFrench = 9,
- Portugese = 10,
+ Portuguese = 10,
Russian = 11,
Korean = 12,
Taiwanese = 13,
diff --git a/src/core/file_sys/fsmitm_romfsbuild.cpp b/src/core/file_sys/fsmitm_romfsbuild.cpp
index 2aff2708a..c52fafb6f 100644
--- a/src/core/file_sys/fsmitm_romfsbuild.cpp
+++ b/src/core/file_sys/fsmitm_romfsbuild.cpp
@@ -266,8 +266,9 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
cur_file->offset = file_partition_size;
file_partition_size += cur_file->size;
cur_file->entry_offset = entry_offset;
- entry_offset += sizeof(RomFSFileEntry) +
- Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4);
+ entry_offset +=
+ static_cast<u32>(sizeof(RomFSFileEntry) +
+ Common::AlignUp(cur_file->path_len - cur_file->cur_path_ofs, 4));
prev_file = cur_file;
}
// Assign deferred parent/sibling ownership.
@@ -284,8 +285,9 @@ std::multimap<u64, VirtualFile> RomFSBuildContext::Build() {
for (const auto& it : directories) {
cur_dir = it.second;
cur_dir->entry_offset = entry_offset;
- entry_offset += sizeof(RomFSDirectoryEntry) +
- Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4);
+ entry_offset +=
+ static_cast<u32>(sizeof(RomFSDirectoryEntry) +
+ Common::AlignUp(cur_dir->path_len - cur_dir->cur_path_ofs, 4));
}
// Assign deferred parent/sibling ownership.
for (auto it = directories.rbegin(); it->second != root; ++it) {
diff --git a/src/core/file_sys/ips_layer.cpp b/src/core/file_sys/ips_layer.cpp
index a08a70efd..a6101f1c0 100644
--- a/src/core/file_sys/ips_layer.cpp
+++ b/src/core/file_sys/ips_layer.cpp
@@ -245,9 +245,11 @@ void IPSwitchCompiler::Parse() {
// Read rest of patch
while (true) {
- if (i + 1 >= lines.size())
+ if (i + 1 >= lines.size()) {
break;
- const auto patch_line = lines[++i];
+ }
+
+ const auto& patch_line = lines[++i];
// Start of new patch
if (StartsWith(patch_line, "@enabled") || StartsWith(patch_line, "@disabled")) {
@@ -297,7 +299,7 @@ void IPSwitchCompiler::Parse() {
patch_text->GetName(), offset, Common::HexToString(replace));
}
- patch.records.insert_or_assign(offset, std::move(replace));
+ patch.records.insert_or_assign(static_cast<u32>(offset), std::move(replace));
}
patches.push_back(std::move(patch));
diff --git a/src/core/file_sys/kernel_executable.cpp b/src/core/file_sys/kernel_executable.cpp
index 76313679d..ef93ef3ed 100644
--- a/src/core/file_sys/kernel_executable.cpp
+++ b/src/core/file_sys/kernel_executable.cpp
@@ -2,9 +2,12 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <cstring>
+
#include "common/string_util.h"
#include "core/file_sys/kernel_executable.h"
#include "core/file_sys/vfs_offset.h"
+#include "core/loader/loader.h"
namespace FileSys {
diff --git a/src/core/file_sys/kernel_executable.h b/src/core/file_sys/kernel_executable.h
index 324a57384..044c554d3 100644
--- a/src/core/file_sys/kernel_executable.h
+++ b/src/core/file_sys/kernel_executable.h
@@ -4,10 +4,17 @@
#pragma once
+#include <array>
+#include <vector>
+
#include "common/common_funcs.h"
+#include "common/common_types.h"
#include "common/swap.h"
#include "core/file_sys/vfs_types.h"
-#include "core/loader/loader.h"
+
+namespace Loader {
+enum class ResultStatus : u16;
+}
namespace FileSys {
diff --git a/src/core/file_sys/nca_metadata.cpp b/src/core/file_sys/nca_metadata.cpp
index 93d0df6b9..3596541b2 100644
--- a/src/core/file_sys/nca_metadata.cpp
+++ b/src/core/file_sys/nca_metadata.cpp
@@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "common/swap.h"
#include "core/file_sys/nca_metadata.h"
+#include "core/file_sys/vfs.h"
namespace FileSys {
@@ -107,7 +108,7 @@ std::vector<u8> CNMT::Serialize() const {
memcpy(out.data() + sizeof(CNMTHeader), &opt_header, sizeof(OptionalHeader));
}
- auto offset = header.table_offset;
+ u64_le offset = header.table_offset;
for (const auto& rec : content_records) {
memcpy(out.data() + offset + sizeof(CNMTHeader), &rec, sizeof(ContentRecord));
diff --git a/src/core/file_sys/nca_metadata.h b/src/core/file_sys/nca_metadata.h
index 1f82fff0a..53535e5f5 100644
--- a/src/core/file_sys/nca_metadata.h
+++ b/src/core/file_sys/nca_metadata.h
@@ -10,7 +10,7 @@
#include "common/common_funcs.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_types.h"
namespace FileSys {
class CNMT;
diff --git a/src/core/file_sys/nca_patch.cpp b/src/core/file_sys/nca_patch.cpp
index fe7375e84..5990a2fd5 100644
--- a/src/core/file_sys/nca_patch.cpp
+++ b/src/core/file_sys/nca_patch.cpp
@@ -12,6 +12,49 @@
#include "core/file_sys/nca_patch.h"
namespace FileSys {
+namespace {
+template <bool Subsection, typename BlockType, typename BucketType>
+std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, const BlockType& block,
+ const BucketType& buckets) {
+ if constexpr (Subsection) {
+ const auto& last_bucket = buckets[block.number_buckets - 1];
+ if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch) {
+ return {block.number_buckets - 1, last_bucket.number_entries};
+ }
+ } else {
+ ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
+ }
+
+ std::size_t bucket_id = std::count_if(
+ block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
+ [&offset](u64 base_offset) { return base_offset <= offset; });
+
+ const auto& bucket = buckets[bucket_id];
+
+ if (bucket.number_entries == 1) {
+ return {bucket_id, 0};
+ }
+
+ std::size_t low = 0;
+ std::size_t mid = 0;
+ std::size_t high = bucket.number_entries - 1;
+ while (low <= high) {
+ mid = (low + high) / 2;
+ if (bucket.entries[mid].address_patch > offset) {
+ high = mid - 1;
+ } else {
+ if (mid == bucket.number_entries - 1 ||
+ bucket.entries[mid + 1].address_patch > offset) {
+ return {bucket_id, mid};
+ }
+
+ low = mid + 1;
+ }
+ }
+
+ UNREACHABLE_MSG("Offset could not be found in BKTR block.");
+}
+} // Anonymous namespace
BKTR::BKTR(VirtualFile base_romfs_, VirtualFile bktr_romfs_, RelocationBlock relocation_,
std::vector<RelocationBucket> relocation_buckets_, SubsectionBlock subsection_,
@@ -110,46 +153,6 @@ std::size_t BKTR::Read(u8* data, std::size_t length, std::size_t offset) const {
return raw_read;
}
-template <bool Subsection, typename BlockType, typename BucketType>
-std::pair<std::size_t, std::size_t> BKTR::SearchBucketEntry(u64 offset, BlockType block,
- BucketType buckets) const {
- if constexpr (Subsection) {
- const auto last_bucket = buckets[block.number_buckets - 1];
- if (offset >= last_bucket.entries[last_bucket.number_entries].address_patch)
- return {block.number_buckets - 1, last_bucket.number_entries};
- } else {
- ASSERT_MSG(offset <= block.size, "Offset is out of bounds in BKTR relocation block.");
- }
-
- std::size_t bucket_id = std::count_if(
- block.base_offsets.begin() + 1, block.base_offsets.begin() + block.number_buckets,
- [&offset](u64 base_offset) { return base_offset <= offset; });
-
- const auto bucket = buckets[bucket_id];
-
- if (bucket.number_entries == 1)
- return {bucket_id, 0};
-
- std::size_t low = 0;
- std::size_t mid = 0;
- std::size_t high = bucket.number_entries - 1;
- while (low <= high) {
- mid = (low + high) / 2;
- if (bucket.entries[mid].address_patch > offset) {
- high = mid - 1;
- } else {
- if (mid == bucket.number_entries - 1 ||
- bucket.entries[mid + 1].address_patch > offset) {
- return {bucket_id, mid};
- }
-
- low = mid + 1;
- }
- }
-
- UNREACHABLE_MSG("Offset could not be found in BKTR block.");
-}
-
RelocationEntry BKTR::GetRelocationEntry(u64 offset) const {
const auto res = SearchBucketEntry<false>(offset, relocation, relocation_buckets);
return relocation_buckets[res.first].entries[res.second];
diff --git a/src/core/file_sys/nca_patch.h b/src/core/file_sys/nca_patch.h
index 8e64e8378..60c544f8e 100644
--- a/src/core/file_sys/nca_patch.h
+++ b/src/core/file_sys/nca_patch.h
@@ -117,10 +117,6 @@ public:
bool Rename(std::string_view name) override;
private:
- template <bool Subsection, typename BlockType, typename BucketType>
- std::pair<std::size_t, std::size_t> SearchBucketEntry(u64 offset, BlockType block,
- BucketType buckets) const;
-
RelocationEntry GetRelocationEntry(u64 offset) const;
RelocationEntry GetNextRelocationEntry(u64 offset) const;
diff --git a/src/core/file_sys/partition_filesystem.cpp b/src/core/file_sys/partition_filesystem.cpp
index 846986736..48a2ed4d4 100644
--- a/src/core/file_sys/partition_filesystem.cpp
+++ b/src/core/file_sys/partition_filesystem.cpp
@@ -21,7 +21,7 @@ bool PartitionFilesystem::Header::HasValidMagicValue() const {
magic == Common::MakeMagic('P', 'F', 'S', '0');
}
-PartitionFilesystem::PartitionFilesystem(std::shared_ptr<VfsFile> file) {
+PartitionFilesystem::PartitionFilesystem(VirtualFile file) {
// At least be as large as the header
if (file->GetSize() < sizeof(Header)) {
status = Loader::ResultStatus::ErrorBadPFSHeader;
@@ -89,11 +89,11 @@ std::map<std::string, u64> PartitionFilesystem::GetFileSizes() const {
return sizes;
}
-std::vector<std::shared_ptr<VfsFile>> PartitionFilesystem::GetFiles() const {
+std::vector<VirtualFile> PartitionFilesystem::GetFiles() const {
return pfs_files;
}
-std::vector<std::shared_ptr<VfsDirectory>> PartitionFilesystem::GetSubdirectories() const {
+std::vector<VirtualDir> PartitionFilesystem::GetSubdirectories() const {
return {};
}
@@ -101,7 +101,7 @@ std::string PartitionFilesystem::GetName() const {
return is_hfs ? "HFS0" : "PFS0";
}
-std::shared_ptr<VfsDirectory> PartitionFilesystem::GetParentDirectory() const {
+VirtualDir PartitionFilesystem::GetParentDirectory() const {
// TODO(DarkLordZach): Add support for nested containers.
return nullptr;
}
diff --git a/src/core/file_sys/partition_filesystem.h b/src/core/file_sys/partition_filesystem.h
index 279193b19..0f831148e 100644
--- a/src/core/file_sys/partition_filesystem.h
+++ b/src/core/file_sys/partition_filesystem.h
@@ -24,7 +24,7 @@ namespace FileSys {
*/
class PartitionFilesystem : public ReadOnlyVfsDirectory {
public:
- explicit PartitionFilesystem(std::shared_ptr<VfsFile> file);
+ explicit PartitionFilesystem(VirtualFile file);
~PartitionFilesystem() override;
Loader::ResultStatus GetStatus() const;
@@ -32,10 +32,10 @@ public:
std::map<std::string, u64> GetFileOffsets() const;
std::map<std::string, u64> GetFileSizes() const;
- std::vector<std::shared_ptr<VfsFile>> GetFiles() const override;
- std::vector<std::shared_ptr<VfsDirectory>> GetSubdirectories() const override;
+ std::vector<VirtualFile> GetFiles() const override;
+ std::vector<VirtualDir> GetSubdirectories() const override;
std::string GetName() const override;
- std::shared_ptr<VfsDirectory> GetParentDirectory() const override;
+ VirtualDir GetParentDirectory() const override;
void PrintDebugInfo() const;
private:
diff --git a/src/core/file_sys/patch_manager.cpp b/src/core/file_sys/patch_manager.cpp
index 729dbb5f4..e9d1607d0 100644
--- a/src/core/file_sys/patch_manager.cpp
+++ b/src/core/file_sys/patch_manager.cpp
@@ -27,8 +27,9 @@
#include "core/settings.h"
namespace FileSys {
+namespace {
-constexpr u64 SINGLE_BYTE_MODULUS = 0x100;
+constexpr u32 SINGLE_BYTE_MODULUS = 0x100;
constexpr u64 DLC_BASE_TITLE_ID_MASK = 0xFFFFFFFFFFFFE000;
constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
@@ -36,21 +37,29 @@ constexpr std::array<const char*, 14> EXEFS_FILE_NAMES{
"subsdk3", "subsdk4", "subsdk5", "subsdk6", "subsdk7", "subsdk8", "subsdk9",
};
-std::string FormatTitleVersion(u32 version, TitleVersionFormat format) {
+enum class TitleVersionFormat : u8 {
+ ThreeElements, ///< vX.Y.Z
+ FourElements, ///< vX.Y.Z.W
+};
+
+std::string FormatTitleVersion(u32 version,
+ TitleVersionFormat format = TitleVersionFormat::ThreeElements) {
std::array<u8, sizeof(u32)> bytes{};
- bytes[0] = version % SINGLE_BYTE_MODULUS;
+ bytes[0] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
for (std::size_t i = 1; i < bytes.size(); ++i) {
version /= SINGLE_BYTE_MODULUS;
- bytes[i] = version % SINGLE_BYTE_MODULUS;
+ bytes[i] = static_cast<u8>(version % SINGLE_BYTE_MODULUS);
}
- if (format == TitleVersionFormat::FourElements)
+ if (format == TitleVersionFormat::FourElements) {
return fmt::format("v{}.{}.{}.{}", bytes[3], bytes[2], bytes[1], bytes[0]);
+ }
return fmt::format("v{}.{}.{}", bytes[3], bytes[2], bytes[1]);
}
-std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir,
- std::string_view name) {
+// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
+// doesn't have a directory with name.
+VirtualDir FindSubdirectoryCaseless(const VirtualDir dir, std::string_view name) {
#ifdef _WIN32
return dir->GetSubdirectory(name);
#else
@@ -66,7 +75,47 @@ std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<Vfs
#endif
}
-PatchManager::PatchManager(u64 title_id) : title_id(title_id) {}
+std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
+ u64 title_id, const PatchManager::BuildID& build_id_, const VirtualDir& base_path, bool upper) {
+ const auto build_id_raw = Common::HexToString(build_id_, upper);
+ const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
+ const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
+
+ if (file == nullptr) {
+ LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
+ title_id, build_id);
+ return std::nullopt;
+ }
+
+ std::vector<u8> data(file->GetSize());
+ if (file->Read(data.data(), data.size()) != data.size()) {
+ LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
+ title_id, build_id);
+ return std::nullopt;
+ }
+
+ const Core::Memory::TextCheatParser parser;
+ return parser.Parse(std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
+}
+
+void AppendCommaIfNotEmpty(std::string& to, std::string_view with) {
+ if (to.empty()) {
+ to += with;
+ } else {
+ to += ", ";
+ to += with;
+ }
+}
+
+bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
+ return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
+}
+} // Anonymous namespace
+
+PatchManager::PatchManager(u64 title_id_,
+ const Service::FileSystem::FileSystemController& fs_controller_,
+ const ContentProvider& content_provider_)
+ : title_id{title_id_}, fs_controller{fs_controller_}, content_provider{content_provider_} {}
PatchManager::~PatchManager() = default;
@@ -82,34 +131,30 @@ VirtualDir PatchManager::PatchExeFS(VirtualDir exefs) const {
if (Settings::values.dump_exefs) {
LOG_INFO(Loader, "Dumping ExeFS for title_id={:016X}", title_id);
- const auto dump_dir =
- Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
+ const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto exefs_dir = GetOrCreateDirectoryRelative(dump_dir, "/exefs");
VfsRawCopyD(exefs, exefs_dir);
}
}
- const auto& installed = Core::System::GetInstance().GetContentProvider();
-
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
std::find(disabled.cbegin(), disabled.cend(), "Update") != disabled.cend();
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
- const auto update = installed.GetEntry(update_tid, ContentRecordType::Program);
+ const auto update = content_provider.GetEntry(update_tid, ContentRecordType::Program);
if (!update_disabled && update != nullptr && update->GetExeFS() != nullptr &&
update->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
LOG_INFO(Loader, " ExeFS: Update ({}) applied successfully",
- FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
+ FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
exefs = update->GetExeFS();
}
// LayeredExeFS
- const auto load_dir =
- Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
+ const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir != nullptr && load_dir->GetSize() > 0) {
auto patch_dirs = load_dir->GetSubdirectories();
std::sort(
@@ -195,8 +240,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
if (Settings::values.dump_nso) {
LOG_INFO(Loader, "Dumping NSO for name={}, build_id={}, title_id={:016X}", name, build_id,
title_id);
- const auto dump_dir =
- Core::System::GetInstance().GetFileSystemController().GetModificationDumpRoot(title_id);
+ const auto dump_dir = fs_controller.GetModificationDumpRoot(title_id);
if (dump_dir != nullptr) {
const auto nso_dir = GetOrCreateDirectoryRelative(dump_dir, "/nso");
const auto file = nso_dir->CreateFile(fmt::format("{}-{}.nso", name, build_id));
@@ -208,8 +252,7 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
LOG_INFO(Loader, "Patching NSO for name={}, build_id={}", name, build_id);
- const auto load_dir =
- Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
+ const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return nso;
@@ -246,14 +289,13 @@ std::vector<u8> PatchManager::PatchNSO(const std::vector<u8>& nso, const std::st
return out;
}
-bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
+bool PatchManager::HasNSOPatch(const BuildID& build_id_) const {
const auto build_id_raw = Common::HexToString(build_id_);
const auto build_id = build_id_raw.substr(0, build_id_raw.find_last_not_of('0') + 1);
LOG_INFO(Loader, "Querying NSO patch existence for build_id={}", build_id);
- const auto load_dir =
- Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
+ const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return false;
@@ -266,37 +308,9 @@ bool PatchManager::HasNSOPatch(const std::array<u8, 32>& build_id_) const {
return !CollectPatches(patch_dirs, build_id).empty();
}
-namespace {
-std::optional<std::vector<Core::Memory::CheatEntry>> ReadCheatFileFromFolder(
- const Core::System& system, u64 title_id, const std::array<u8, 0x20>& build_id_,
- const VirtualDir& base_path, bool upper) {
- const auto build_id_raw = Common::HexToString(build_id_, upper);
- const auto build_id = build_id_raw.substr(0, sizeof(u64) * 2);
- const auto file = base_path->GetFile(fmt::format("{}.txt", build_id));
-
- if (file == nullptr) {
- LOG_INFO(Common_Filesystem, "No cheats file found for title_id={:016X}, build_id={}",
- title_id, build_id);
- return std::nullopt;
- }
-
- std::vector<u8> data(file->GetSize());
- if (file->Read(data.data(), data.size()) != data.size()) {
- LOG_INFO(Common_Filesystem, "Failed to read cheats file for title_id={:016X}, build_id={}",
- title_id, build_id);
- return std::nullopt;
- }
-
- Core::Memory::TextCheatParser parser;
- return parser.Parse(system,
- std::string_view(reinterpret_cast<const char*>(data.data()), data.size()));
-}
-
-} // Anonymous namespace
-
std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
- const Core::System& system, const std::array<u8, 32>& build_id_) const {
- const auto load_dir = system.GetFileSystemController().GetModificationLoadRoot(title_id);
+ const BuildID& build_id_) const {
+ const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if (load_dir == nullptr) {
LOG_ERROR(Loader, "Cannot load mods for invalid title_id={:016X}", title_id);
return {};
@@ -315,14 +329,12 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
auto cheats_dir = FindSubdirectoryCaseless(subdir, "cheats");
if (cheats_dir != nullptr) {
- auto res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, true);
- if (res.has_value()) {
+ if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, true)) {
std::copy(res->begin(), res->end(), std::back_inserter(out));
continue;
}
- res = ReadCheatFileFromFolder(system, title_id, build_id_, cheats_dir, false);
- if (res.has_value()) {
+ if (const auto res = ReadCheatFileFromFolder(title_id, build_id_, cheats_dir, false)) {
std::copy(res->begin(), res->end(), std::back_inserter(out));
}
}
@@ -331,9 +343,9 @@ std::vector<Core::Memory::CheatEntry> PatchManager::CreateCheatList(
return out;
}
-static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type) {
- const auto load_dir =
- Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
+static void ApplyLayeredFS(VirtualFile& romfs, u64 title_id, ContentRecordType type,
+ const Service::FileSystem::FileSystemController& fs_controller) {
+ const auto load_dir = fs_controller.GetModificationLoadRoot(title_id);
if ((type != ContentRecordType::Program && type != ContentRecordType::Data) ||
load_dir == nullptr || load_dir->GetSize() <= 0) {
return;
@@ -395,19 +407,19 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
const auto log_string = fmt::format("Patching RomFS for title_id={:016X}, type={:02X}",
title_id, static_cast<u8>(type));
- if (type == ContentRecordType::Program || type == ContentRecordType::Data)
+ if (type == ContentRecordType::Program || type == ContentRecordType::Data) {
LOG_INFO(Loader, "{}", log_string);
- else
+ } else {
LOG_DEBUG(Loader, "{}", log_string);
+ }
- if (romfs == nullptr)
+ if (romfs == nullptr) {
return romfs;
-
- const auto& installed = Core::System::GetInstance().GetContentProvider();
+ }
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
- const auto update = installed.GetEntryRaw(update_tid, type);
+ const auto update = content_provider.GetEntryRaw(update_tid, type);
const auto& disabled = Settings::values.disabled_addons[title_id];
const auto update_disabled =
@@ -418,7 +430,7 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
if (new_nca->GetStatus() == Loader::ResultStatus::Success &&
new_nca->GetRomFS() != nullptr) {
LOG_INFO(Loader, " RomFS: Update ({}) applied successfully",
- FormatTitleVersion(installed.GetEntryVersion(update_tid).value_or(0)));
+ FormatTitleVersion(content_provider.GetEntryVersion(update_tid).value_or(0)));
romfs = new_nca->GetRomFS();
}
} else if (!update_disabled && update_raw != nullptr) {
@@ -431,33 +443,22 @@ VirtualFile PatchManager::PatchRomFS(VirtualFile romfs, u64 ivfc_offset, Content
}
// LayeredFS
- ApplyLayeredFS(romfs, title_id, type);
+ ApplyLayeredFS(romfs, title_id, type, fs_controller);
return romfs;
}
-static void AppendCommaIfNotEmpty(std::string& to, const std::string& with) {
- if (to.empty())
- to += with;
- else
- to += ", " + with;
-}
-
-static bool IsDirValidAndNonEmpty(const VirtualDir& dir) {
- return dir != nullptr && (!dir->GetFiles().empty() || !dir->GetSubdirectories().empty());
-}
-
-std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNames(
- VirtualFile update_raw) const {
- if (title_id == 0)
+PatchManager::PatchVersionNames PatchManager::GetPatchVersionNames(VirtualFile update_raw) const {
+ if (title_id == 0) {
return {};
+ }
+
std::map<std::string, std::string, std::less<>> out;
- const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto& disabled = Settings::values.disabled_addons[title_id];
// Game Updates
const auto update_tid = GetUpdateTitleID(title_id);
- PatchManager update{update_tid};
+ PatchManager update{update_tid, fs_controller, content_provider};
const auto metadata = update.GetControlMetadata();
const auto& nacp = metadata.first;
@@ -468,13 +469,12 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
if (nacp != nullptr) {
out.insert_or_assign(update_label, nacp->GetVersionString());
} else {
- if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
- const auto meta_ver = installed.GetEntryVersion(update_tid);
+ if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
+ const auto meta_ver = content_provider.GetEntryVersion(update_tid);
if (meta_ver.value_or(0) == 0) {
out.insert_or_assign(update_label, "");
} else {
- out.insert_or_assign(
- update_label, FormatTitleVersion(*meta_ver, TitleVersionFormat::ThreeElements));
+ out.insert_or_assign(update_label, FormatTitleVersion(*meta_ver));
}
} else if (update_raw != nullptr) {
out.insert_or_assign(update_label, "PACKED");
@@ -482,8 +482,7 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
// General Mods (LayeredFS and IPS)
- const auto mod_dir =
- Core::System::GetInstance().GetFileSystemController().GetModificationLoadRoot(title_id);
+ const auto mod_dir = fs_controller.GetModificationLoadRoot(title_id);
if (mod_dir != nullptr && mod_dir->GetSize() > 0) {
for (const auto& mod : mod_dir->GetSubdirectories()) {
std::string types;
@@ -527,13 +526,15 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
// DLC
- const auto dlc_entries = installed.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
+ const auto dlc_entries =
+ content_provider.ListEntriesFilter(TitleType::AOC, ContentRecordType::Data);
std::vector<ContentProviderEntry> dlc_match;
dlc_match.reserve(dlc_entries.size());
std::copy_if(dlc_entries.begin(), dlc_entries.end(), std::back_inserter(dlc_match),
- [this, &installed](const ContentProviderEntry& entry) {
+ [this](const ContentProviderEntry& entry) {
return (entry.title_id & DLC_BASE_TITLE_ID_MASK) == title_id &&
- installed.GetEntry(entry)->GetStatus() == Loader::ResultStatus::Success;
+ content_provider.GetEntry(entry)->GetStatus() ==
+ Loader::ResultStatus::Success;
});
if (!dlc_match.empty()) {
// Ensure sorted so DLC IDs show in order.
@@ -554,49 +555,52 @@ std::map<std::string, std::string, std::less<>> PatchManager::GetPatchVersionNam
}
std::optional<u32> PatchManager::GetGameVersion() const {
- const auto& installed = Core::System::GetInstance().GetContentProvider();
const auto update_tid = GetUpdateTitleID(title_id);
- if (installed.HasEntry(update_tid, ContentRecordType::Program)) {
- return installed.GetEntryVersion(update_tid);
+ if (content_provider.HasEntry(update_tid, ContentRecordType::Program)) {
+ return content_provider.GetEntryVersion(update_tid);
}
- return installed.GetEntryVersion(title_id);
+ return content_provider.GetEntryVersion(title_id);
}
-std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::GetControlMetadata() const {
- const auto& installed = Core::System::GetInstance().GetContentProvider();
-
- const auto base_control_nca = installed.GetEntry(title_id, ContentRecordType::Control);
- if (base_control_nca == nullptr)
+PatchManager::Metadata PatchManager::GetControlMetadata() const {
+ const auto base_control_nca = content_provider.GetEntry(title_id, ContentRecordType::Control);
+ if (base_control_nca == nullptr) {
return {};
+ }
return ParseControlNCA(*base_control_nca);
}
-std::pair<std::unique_ptr<NACP>, VirtualFile> PatchManager::ParseControlNCA(const NCA& nca) const {
+PatchManager::Metadata PatchManager::ParseControlNCA(const NCA& nca) const {
const auto base_romfs = nca.GetRomFS();
- if (base_romfs == nullptr)
+ if (base_romfs == nullptr) {
return {};
+ }
const auto romfs = PatchRomFS(base_romfs, nca.GetBaseIVFCOffset(), ContentRecordType::Control);
- if (romfs == nullptr)
+ if (romfs == nullptr) {
return {};
+ }
const auto extracted = ExtractRomFS(romfs);
- if (extracted == nullptr)
+ if (extracted == nullptr) {
return {};
+ }
auto nacp_file = extracted->GetFile("control.nacp");
- if (nacp_file == nullptr)
+ if (nacp_file == nullptr) {
nacp_file = extracted->GetFile("Control.nacp");
+ }
auto nacp = nacp_file == nullptr ? nullptr : std::make_unique<NACP>(nacp_file);
VirtualFile icon_file;
for (const auto& language : FileSys::LANGUAGE_NAMES) {
- icon_file = extracted->GetFile("icon_" + std::string(language) + ".dat");
- if (icon_file != nullptr)
+ icon_file = extracted->GetFile(std::string("icon_").append(language).append(".dat"));
+ if (icon_file != nullptr) {
break;
+ }
}
return {std::move(nacp), icon_file};
diff --git a/src/core/file_sys/patch_manager.h b/src/core/file_sys/patch_manager.h
index f4cb918dd..fb1853035 100644
--- a/src/core/file_sys/patch_manager.h
+++ b/src/core/file_sys/patch_manager.h
@@ -6,88 +6,89 @@
#include <map>
#include <memory>
+#include <optional>
#include <string>
#include "common/common_types.h"
#include "core/file_sys/nca_metadata.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_types.h"
#include "core/memory/dmnt_cheat_types.h"
namespace Core {
class System;
}
+namespace Service::FileSystem {
+class FileSystemController;
+}
+
namespace FileSys {
+class ContentProvider;
class NCA;
class NACP;
-enum class TitleVersionFormat : u8 {
- ThreeElements, ///< vX.Y.Z
- FourElements, ///< vX.Y.Z.W
-};
-
-std::string FormatTitleVersion(u32 version,
- TitleVersionFormat format = TitleVersionFormat::ThreeElements);
-
-// Returns a directory with name matching name case-insensitive. Returns nullptr if directory
-// doesn't have a directory with name.
-std::shared_ptr<VfsDirectory> FindSubdirectoryCaseless(const std::shared_ptr<VfsDirectory> dir,
- std::string_view name);
-
// A centralized class to manage patches to games.
class PatchManager {
public:
- explicit PatchManager(u64 title_id);
+ using BuildID = std::array<u8, 0x20>;
+ using Metadata = std::pair<std::unique_ptr<NACP>, VirtualFile>;
+ using PatchVersionNames = std::map<std::string, std::string, std::less<>>;
+
+ explicit PatchManager(u64 title_id_,
+ const Service::FileSystem::FileSystemController& fs_controller_,
+ const ContentProvider& content_provider_);
~PatchManager();
- u64 GetTitleID() const;
+ [[nodiscard]] u64 GetTitleID() const;
// Currently tracked ExeFS patches:
// - Game Updates
- VirtualDir PatchExeFS(VirtualDir exefs) const;
+ [[nodiscard]] VirtualDir PatchExeFS(VirtualDir exefs) const;
// Currently tracked NSO patches:
// - IPS
// - IPSwitch
- std::vector<u8> PatchNSO(const std::vector<u8>& nso, const std::string& name) const;
+ [[nodiscard]] std::vector<u8> PatchNSO(const std::vector<u8>& nso,
+ const std::string& name) const;
// Checks to see if PatchNSO() will have any effect given the NSO's build ID.
// Used to prevent expensive copies in NSO loader.
- bool HasNSOPatch(const std::array<u8, 0x20>& build_id) const;
+ [[nodiscard]] bool HasNSOPatch(const BuildID& build_id) const;
// Creates a CheatList object with all
- std::vector<Core::Memory::CheatEntry> CreateCheatList(
- const Core::System& system, const std::array<u8, 0x20>& build_id) const;
+ [[nodiscard]] std::vector<Core::Memory::CheatEntry> CreateCheatList(
+ const BuildID& build_id) const;
// Currently tracked RomFS patches:
// - Game Updates
// - LayeredFS
- VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
- ContentRecordType type = ContentRecordType::Program,
- VirtualFile update_raw = nullptr) const;
+ [[nodiscard]] VirtualFile PatchRomFS(VirtualFile base, u64 ivfc_offset,
+ ContentRecordType type = ContentRecordType::Program,
+ VirtualFile update_raw = nullptr) const;
// Returns a vector of pairs between patch names and patch versions.
// i.e. Update 3.2.2 will return {"Update", "3.2.2"}
- std::map<std::string, std::string, std::less<>> GetPatchVersionNames(
- VirtualFile update_raw = nullptr) const;
+ [[nodiscard]] PatchVersionNames GetPatchVersionNames(VirtualFile update_raw = nullptr) const;
// If the game update exists, returns the u32 version field in its Meta-type NCA. If that fails,
// it will fallback to the Meta-type NCA of the base game. If that fails, the result will be
// std::nullopt
- std::optional<u32> GetGameVersion() const;
+ [[nodiscard]] std::optional<u32> GetGameVersion() const;
// Given title_id of the program, attempts to get the control data of the update and parse
// it, falling back to the base control data.
- std::pair<std::unique_ptr<NACP>, VirtualFile> GetControlMetadata() const;
+ [[nodiscard]] Metadata GetControlMetadata() const;
// Version of GetControlMetadata that takes an arbitrary NCA
- std::pair<std::unique_ptr<NACP>, VirtualFile> ParseControlNCA(const NCA& nca) const;
+ [[nodiscard]] Metadata ParseControlNCA(const NCA& nca) const;
private:
- std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
- const std::string& build_id) const;
+ [[nodiscard]] std::vector<VirtualFile> CollectPatches(const std::vector<VirtualDir>& patch_dirs,
+ const std::string& build_id) const;
u64 title_id;
+ const Service::FileSystem::FileSystemController& fs_controller;
+ const ContentProvider& content_provider;
};
} // namespace FileSys
diff --git a/src/core/file_sys/program_metadata.cpp b/src/core/file_sys/program_metadata.cpp
index 43169bf9f..9cf49bf44 100644
--- a/src/core/file_sys/program_metadata.cpp
+++ b/src/core/file_sys/program_metadata.cpp
@@ -7,6 +7,7 @@
#include "common/logging/log.h"
#include "core/file_sys/program_metadata.h"
+#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
namespace FileSys {
diff --git a/src/core/file_sys/program_metadata.h b/src/core/file_sys/program_metadata.h
index 35069972b..455532567 100644
--- a/src/core/file_sys/program_metadata.h
+++ b/src/core/file_sys/program_metadata.h
@@ -9,7 +9,7 @@
#include "common/bit_field.h"
#include "common/common_types.h"
#include "common/swap.h"
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_types.h"
namespace Loader {
enum class ResultStatus : u16;
diff --git a/src/core/file_sys/registered_cache.cpp b/src/core/file_sys/registered_cache.cpp
index e42b677f7..da01002d5 100644
--- a/src/core/file_sys/registered_cache.cpp
+++ b/src/core/file_sys/registered_cache.cpp
@@ -257,8 +257,7 @@ std::vector<NcaID> PlaceholderCache::List() const {
for (const auto& sdir : dir->GetSubdirectories()) {
for (const auto& file : sdir->GetFiles()) {
const auto name = file->GetName();
- if (name.length() == 36 && name[32] == '.' && name[33] == 'n' && name[34] == 'c' &&
- name[35] == 'a') {
+ if (name.length() == 36 && name.ends_with(".nca")) {
out.push_back(Common::HexStringToArray<0x10>(name.substr(0, 32)));
}
}
@@ -621,25 +620,25 @@ InstallResult RegisteredCache::InstallEntry(const NSP& nsp, bool overwrite_if_ex
InstallResult RegisteredCache::InstallEntry(const NCA& nca, TitleType type,
bool overwrite_if_exists, const VfsCopyFunction& copy) {
- CNMTHeader header{
- nca.GetTitleId(), // Title ID
- 0, // Ignore/Default title version
- type, // Type
- {}, // Padding
- 0x10, // Default table offset
- 1, // 1 Content Entry
- 0, // No Meta Entries
- {}, // Padding
- {}, // Reserved 1
- 0, // Is committed
- 0, // Required download system version
- {}, // Reserved 2
+ const CNMTHeader header{
+ .title_id = nca.GetTitleId(),
+ .title_version = 0,
+ .type = type,
+ .reserved = {},
+ .table_offset = 0x10,
+ .number_content_entries = 1,
+ .number_meta_entries = 0,
+ .attributes = 0,
+ .reserved2 = {},
+ .is_committed = 0,
+ .required_download_system_version = 0,
+ .reserved3 = {},
};
- OptionalHeader opt_header{0, 0};
+ const OptionalHeader opt_header{0, 0};
ContentRecord c_rec{{}, {}, {}, GetCRTypeFromNCAType(nca.GetType()), {}};
const auto& data = nca.GetBaseFile()->ReadBytes(0x100000);
mbedtls_sha256_ret(data.data(), data.size(), c_rec.hash.data(), 0);
- memcpy(&c_rec.nca_id, &c_rec.hash, 16);
+ std::memcpy(&c_rec.nca_id, &c_rec.hash, 16);
const CNMT new_cnmt(header, opt_header, {c_rec}, {});
if (!RawInstallYuzuMeta(new_cnmt)) {
return InstallResult::ErrorMetaFailed;
diff --git a/src/core/file_sys/romfs.h b/src/core/file_sys/romfs.h
index 2fd07ed04..82e683782 100644
--- a/src/core/file_sys/romfs.h
+++ b/src/core/file_sys/romfs.h
@@ -4,7 +4,6 @@
#pragma once
-#include <array>
#include "core/file_sys/vfs.h"
namespace FileSys {
diff --git a/src/core/file_sys/romfs_factory.cpp b/src/core/file_sys/romfs_factory.cpp
index 418a39a7e..987199747 100644
--- a/src/core/file_sys/romfs_factory.cpp
+++ b/src/core/file_sys/romfs_factory.cpp
@@ -6,7 +6,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/file_sys/card_image.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/nca_metadata.h"
@@ -19,7 +18,9 @@
namespace FileSys {
-RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader) {
+RomFSFactory::RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider,
+ Service::FileSystem::FileSystemController& controller)
+ : content_provider{provider}, filesystem_controller{controller} {
// Load the RomFS from the app
if (app_loader.ReadRomFS(file) != Loader::ResultStatus::Success) {
LOG_ERROR(Service_FS, "Unable to read RomFS!");
@@ -36,49 +37,50 @@ void RomFSFactory::SetPackedUpdate(VirtualFile update_raw) {
}
ResultVal<VirtualFile> RomFSFactory::OpenCurrentProcess(u64 current_process_title_id) const {
- if (!updatable)
+ if (!updatable) {
return MakeResult<VirtualFile>(file);
+ }
- const PatchManager patch_manager(current_process_title_id);
+ const PatchManager patch_manager{current_process_title_id, filesystem_controller,
+ content_provider};
return MakeResult<VirtualFile>(
patch_manager.PatchRomFS(file, ivfc_offset, ContentRecordType::Program, update_raw));
}
ResultVal<VirtualFile> RomFSFactory::Open(u64 title_id, StorageId storage,
ContentRecordType type) const {
- std::shared_ptr<NCA> res;
-
- switch (storage) {
- case StorageId::None:
- res = Core::System::GetInstance().GetContentProvider().GetEntry(title_id, type);
- break;
- case StorageId::NandSystem:
- res =
- Core::System::GetInstance().GetFileSystemController().GetSystemNANDContents()->GetEntry(
- title_id, type);
- break;
- case StorageId::NandUser:
- res = Core::System::GetInstance().GetFileSystemController().GetUserNANDContents()->GetEntry(
- title_id, type);
- break;
- case StorageId::SdCard:
- res = Core::System::GetInstance().GetFileSystemController().GetSDMCContents()->GetEntry(
- title_id, type);
- break;
- default:
- UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
- }
-
+ const std::shared_ptr<NCA> res = GetEntry(title_id, storage, type);
if (res == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return RESULT_UNKNOWN;
}
+
const auto romfs = res->GetRomFS();
if (romfs == nullptr) {
// TODO(DarkLordZach): Find the right error code to use here
return RESULT_UNKNOWN;
}
+
return MakeResult<VirtualFile>(romfs);
}
+std::shared_ptr<NCA> RomFSFactory::GetEntry(u64 title_id, StorageId storage,
+ ContentRecordType type) const {
+ switch (storage) {
+ case StorageId::None:
+ return content_provider.GetEntry(title_id, type);
+ case StorageId::NandSystem:
+ return filesystem_controller.GetSystemNANDContents()->GetEntry(title_id, type);
+ case StorageId::NandUser:
+ return filesystem_controller.GetUserNANDContents()->GetEntry(title_id, type);
+ case StorageId::SdCard:
+ return filesystem_controller.GetSDMCContents()->GetEntry(title_id, type);
+ case StorageId::Host:
+ case StorageId::GameCard:
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented storage_id={:02X}", static_cast<u8>(storage));
+ return nullptr;
+ }
+}
+
} // namespace FileSys
diff --git a/src/core/file_sys/romfs_factory.h b/src/core/file_sys/romfs_factory.h
index c5d40285c..ec704dfa8 100644
--- a/src/core/file_sys/romfs_factory.h
+++ b/src/core/file_sys/romfs_factory.h
@@ -13,8 +13,15 @@ namespace Loader {
class AppLoader;
} // namespace Loader
+namespace Service::FileSystem {
+class FileSystemController;
+}
+
namespace FileSys {
+class ContentProvider;
+class NCA;
+
enum class ContentRecordType : u8;
enum class StorageId : u8 {
@@ -29,18 +36,26 @@ enum class StorageId : u8 {
/// File system interface to the RomFS archive
class RomFSFactory {
public:
- explicit RomFSFactory(Loader::AppLoader& app_loader);
+ explicit RomFSFactory(Loader::AppLoader& app_loader, ContentProvider& provider,
+ Service::FileSystem::FileSystemController& controller);
~RomFSFactory();
void SetPackedUpdate(VirtualFile update_raw);
- ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
- ResultVal<VirtualFile> Open(u64 title_id, StorageId storage, ContentRecordType type) const;
+ [[nodiscard]] ResultVal<VirtualFile> OpenCurrentProcess(u64 current_process_title_id) const;
+ [[nodiscard]] ResultVal<VirtualFile> Open(u64 title_id, StorageId storage,
+ ContentRecordType type) const;
private:
+ [[nodiscard]] std::shared_ptr<NCA> GetEntry(u64 title_id, StorageId storage,
+ ContentRecordType type) const;
+
VirtualFile file;
VirtualFile update_raw;
bool updatable;
u64 ivfc_offset;
+
+ ContentProvider& content_provider;
+ Service::FileSystem::FileSystemController& filesystem_controller;
};
} // namespace FileSys
diff --git a/src/core/file_sys/sdmc_factory.cpp b/src/core/file_sys/sdmc_factory.cpp
index 6f732e4d8..cb56d8f2d 100644
--- a/src/core/file_sys/sdmc_factory.cpp
+++ b/src/core/file_sys/sdmc_factory.cpp
@@ -5,8 +5,8 @@
#include <memory>
#include "core/file_sys/registered_cache.h"
#include "core/file_sys/sdmc_factory.h"
+#include "core/file_sys/vfs.h"
#include "core/file_sys/xts_archive.h"
-#include "core/settings.h"
namespace FileSys {
diff --git a/src/core/file_sys/sdmc_factory.h b/src/core/file_sys/sdmc_factory.h
index 42dc4e08a..2bb92ba93 100644
--- a/src/core/file_sys/sdmc_factory.h
+++ b/src/core/file_sys/sdmc_factory.h
@@ -5,7 +5,7 @@
#pragma once
#include <memory>
-#include "core/file_sys/vfs.h"
+#include "core/file_sys/vfs_types.h"
#include "core/hle/result.h"
namespace FileSys {
diff --git a/src/core/file_sys/submission_package.cpp b/src/core/file_sys/submission_package.cpp
index 175a8266a..90641d23b 100644
--- a/src/core/file_sys/submission_package.cpp
+++ b/src/core/file_sys/submission_package.cpp
@@ -19,42 +19,10 @@
#include "core/loader/loader.h"
namespace FileSys {
-namespace {
-void SetTicketKeys(const std::vector<VirtualFile>& files) {
- auto& keys = Core::Crypto::KeyManager::Instance();
-
- for (const auto& ticket_file : files) {
- if (ticket_file == nullptr) {
- continue;
- }
-
- if (ticket_file->GetExtension() != "tik") {
- continue;
- }
-
- if (ticket_file->GetSize() <
- Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
- continue;
- }
-
- Core::Crypto::Key128 key{};
- ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
-
- // We get the name without the extension in order to create the rights ID.
- std::string name_only(ticket_file->GetName());
- name_only.erase(name_only.size() - 4);
-
- const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
- u128 rights_id;
- std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
- keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
- }
-}
-} // Anonymous namespace
NSP::NSP(VirtualFile file_)
: file(std::move(file_)), status{Loader::ResultStatus::Success},
- pfs(std::make_shared<PartitionFilesystem>(file)) {
+ pfs(std::make_shared<PartitionFilesystem>(file)), keys{Core::Crypto::KeyManager::Instance()} {
if (pfs->GetStatus() != Loader::ResultStatus::Success) {
status = pfs->GetStatus();
return;
@@ -232,6 +200,35 @@ VirtualDir NSP::GetParentDirectory() const {
return file->GetContainingDirectory();
}
+void NSP::SetTicketKeys(const std::vector<VirtualFile>& files) {
+ for (const auto& ticket_file : files) {
+ if (ticket_file == nullptr) {
+ continue;
+ }
+
+ if (ticket_file->GetExtension() != "tik") {
+ continue;
+ }
+
+ if (ticket_file->GetSize() <
+ Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET + sizeof(Core::Crypto::Key128)) {
+ continue;
+ }
+
+ Core::Crypto::Key128 key{};
+ ticket_file->Read(key.data(), key.size(), Core::Crypto::TICKET_FILE_TITLEKEY_OFFSET);
+
+ // We get the name without the extension in order to create the rights ID.
+ std::string name_only(ticket_file->GetName());
+ name_only.erase(name_only.size() - 4);
+
+ const auto rights_id_raw = Common::HexStringToArray<16>(name_only);
+ u128 rights_id;
+ std::memcpy(rights_id.data(), rights_id_raw.data(), sizeof(u128));
+ keys.SetKey(Core::Crypto::S128KeyType::Titlekey, key, rights_id[1], rights_id[0]);
+ }
+}
+
void NSP::InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files) {
exefs = pfs;
@@ -267,9 +264,9 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
}
const CNMT cnmt(inner_file);
- auto& ncas_title = ncas[cnmt.GetTitleID()];
- ncas_title[{cnmt.GetType(), ContentRecordType::Meta}] = nca;
+ ncas[cnmt.GetTitleID()][{cnmt.GetType(), ContentRecordType::Meta}] = nca;
+
for (const auto& rec : cnmt.GetContentRecords()) {
const auto id_string = Common::HexToString(rec.nca_id, false);
auto next_file = pfs->GetFile(fmt::format("{}.nca", id_string));
@@ -286,13 +283,32 @@ void NSP::ReadNCAs(const std::vector<VirtualFile>& files) {
}
auto next_nca = std::make_shared<NCA>(std::move(next_file), nullptr, 0);
+
if (next_nca->GetType() == NCAContentType::Program) {
- program_status[cnmt.GetTitleID()] = next_nca->GetStatus();
+ program_status[next_nca->GetTitleId()] = next_nca->GetStatus();
}
- if (next_nca->GetStatus() == Loader::ResultStatus::Success ||
- (next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS &&
- (cnmt.GetTitleID() & 0x800) != 0)) {
- ncas_title[{cnmt.GetType(), rec.type}] = std::move(next_nca);
+
+ if (next_nca->GetStatus() != Loader::ResultStatus::Success &&
+ next_nca->GetStatus() != Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
+ continue;
+ }
+
+ // If the last 3 hexadecimal digits of the CNMT TitleID is 0x800 or is missing the
+ // BKTRBaseRomFS, this is an update NCA. Otherwise, this is a base NCA.
+ if ((cnmt.GetTitleID() & 0x800) != 0 ||
+ next_nca->GetStatus() == Loader::ResultStatus::ErrorMissingBKTRBaseRomFS) {
+ // If the last 3 hexadecimal digits of the NCA's TitleID is between 0x1 and
+ // 0x7FF, this is a multi-program update NCA. Otherwise, this is a regular
+ // update NCA.
+ if ((next_nca->GetTitleId() & 0x7FF) != 0 &&
+ (next_nca->GetTitleId() & 0x800) == 0) {
+ ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] =
+ std::move(next_nca);
+ } else {
+ ncas[cnmt.GetTitleID()][{cnmt.GetType(), rec.type}] = std::move(next_nca);
+ }
+ } else {
+ ncas[next_nca->GetTitleId()][{cnmt.GetType(), rec.type}] = std::move(next_nca);
}
}
diff --git a/src/core/file_sys/submission_package.h b/src/core/file_sys/submission_package.h
index cf89de6a9..c70a11b5b 100644
--- a/src/core/file_sys/submission_package.h
+++ b/src/core/file_sys/submission_package.h
@@ -10,6 +10,10 @@
#include "common/common_types.h"
#include "core/file_sys/vfs.h"
+namespace Core::Crypto {
+class KeyManager;
+}
+
namespace Loader {
enum class ResultStatus : u16;
}
@@ -59,6 +63,7 @@ public:
VirtualDir GetParentDirectory() const override;
private:
+ void SetTicketKeys(const std::vector<VirtualFile>& files);
void InitializeExeFSAndRomFS(const std::vector<VirtualFile>& files);
void ReadNCAs(const std::vector<VirtualFile>& files);
@@ -73,7 +78,7 @@ private:
std::map<u64, std::map<std::pair<TitleType, ContentRecordType>, std::shared_ptr<NCA>>> ncas;
std::vector<VirtualFile> ticket_files;
- Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
+ Core::Crypto::KeyManager& keys;
VirtualFile romfs;
VirtualDir exefs;
diff --git a/src/core/file_sys/vfs.cpp b/src/core/file_sys/vfs.cpp
index a4c3f67c4..b2f026b6d 100644
--- a/src/core/file_sys/vfs.cpp
+++ b/src/core/file_sys/vfs.cpp
@@ -169,11 +169,12 @@ VfsDirectory::~VfsDirectory() = default;
std::optional<u8> VfsFile::ReadByte(std::size_t offset) const {
u8 out{};
- std::size_t size = Read(&out, 1, offset);
- if (size == 1)
+ const std::size_t size = Read(&out, sizeof(u8), offset);
+ if (size == 1) {
return out;
+ }
- return {};
+ return std::nullopt;
}
std::vector<u8> VfsFile::ReadBytes(std::size_t size, std::size_t offset) const {
diff --git a/src/core/file_sys/vfs_offset.cpp b/src/core/file_sys/vfs_offset.cpp
index c96f88488..7714d3de5 100644
--- a/src/core/file_sys/vfs_offset.cpp
+++ b/src/core/file_sys/vfs_offset.cpp
@@ -58,10 +58,11 @@ std::size_t OffsetVfsFile::Write(const u8* data, std::size_t length, std::size_t
}
std::optional<u8> OffsetVfsFile::ReadByte(std::size_t r_offset) const {
- if (r_offset < size)
- return file->ReadByte(offset + r_offset);
+ if (r_offset >= size) {
+ return std::nullopt;
+ }
- return {};
+ return file->ReadByte(offset + r_offset);
}
std::vector<u8> OffsetVfsFile::ReadBytes(std::size_t r_size, std::size_t r_offset) const {
diff --git a/src/core/file_sys/vfs_static.h b/src/core/file_sys/vfs_static.h
index 9f5a90b1b..8b27c30fa 100644
--- a/src/core/file_sys/vfs_static.h
+++ b/src/core/file_sys/vfs_static.h
@@ -54,9 +54,11 @@ public:
}
std::optional<u8> ReadByte(std::size_t offset) const override {
- if (offset < size)
- return value;
- return {};
+ if (offset >= size) {
+ return std::nullopt;
+ }
+
+ return value;
}
std::vector<u8> ReadBytes(std::size_t length, std::size_t offset) const override {
diff --git a/src/core/file_sys/xts_archive.cpp b/src/core/file_sys/xts_archive.cpp
index ccf5966d0..24c58e7ae 100644
--- a/src/core/file_sys/xts_archive.cpp
+++ b/src/core/file_sys/xts_archive.cpp
@@ -15,8 +15,9 @@
#include "common/hex_util.h"
#include "common/string_util.h"
#include "core/crypto/aes_util.h"
+#include "core/crypto/key_manager.h"
#include "core/crypto/xts_encryption_layer.h"
-#include "core/file_sys/partition_filesystem.h"
+#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs_offset.h"
#include "core/file_sys/xts_archive.h"
#include "core/loader/loader.h"
@@ -43,7 +44,9 @@ static bool CalculateHMAC256(Destination* out, const SourceKey* key, std::size_t
return true;
}
-NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
+NAX::NAX(VirtualFile file_)
+ : header(std::make_unique<NAXHeader>()),
+ file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
std::string path = Common::FS::SanitizePath(file->GetFullPath());
static const std::regex nax_path_regex("/registered/(000000[0-9A-F]{2})/([0-9A-F]{32})\\.nca",
std::regex_constants::ECMAScript |
@@ -60,7 +63,8 @@ NAX::NAX(VirtualFile file_) : header(std::make_unique<NAXHeader>()), file(std::m
}
NAX::NAX(VirtualFile file_, std::array<u8, 0x10> nca_id)
- : header(std::make_unique<NAXHeader>()), file(std::move(file_)) {
+ : header(std::make_unique<NAXHeader>()),
+ file(std::move(file_)), keys{Core::Crypto::KeyManager::Instance()} {
Core::Crypto::SHA256Hash hash{};
mbedtls_sha256_ret(nca_id.data(), nca_id.size(), hash.data(), 0);
status = Parse(fmt::format("/registered/000000{:02X}/{}.nca", hash[0],
diff --git a/src/core/file_sys/xts_archive.h b/src/core/file_sys/xts_archive.h
index 563531bb6..c472e226e 100644
--- a/src/core/file_sys/xts_archive.h
+++ b/src/core/file_sys/xts_archive.h
@@ -9,12 +9,16 @@
#include "common/common_types.h"
#include "common/swap.h"
#include "core/crypto/key_manager.h"
-#include "core/file_sys/content_archive.h"
#include "core/file_sys/vfs.h"
-#include "core/loader/loader.h"
+
+namespace Loader {
+enum class ResultStatus : u16;
+}
namespace FileSys {
+class NCA;
+
struct NAXHeader {
std::array<u8, 0x20> hmac;
u64_le magic;
@@ -62,6 +66,6 @@ private:
VirtualFile dec_file;
- Core::Crypto::KeyManager& keys = Core::Crypto::KeyManager::Instance();
+ Core::Crypto::KeyManager& keys;
};
} // namespace FileSys
diff --git a/src/core/frontend/applets/controller.cpp b/src/core/frontend/applets/controller.cpp
new file mode 100644
index 000000000..03bbedf8b
--- /dev/null
+++ b/src/core/frontend/applets/controller.cpp
@@ -0,0 +1,81 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "core/frontend/applets/controller.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Core::Frontend {
+
+ControllerApplet::~ControllerApplet() = default;
+
+DefaultControllerApplet::DefaultControllerApplet(Service::SM::ServiceManager& service_manager_)
+ : service_manager{service_manager_} {}
+
+DefaultControllerApplet::~DefaultControllerApplet() = default;
+
+void DefaultControllerApplet::ReconfigureControllers(std::function<void()> callback,
+ const ControllerParameters& parameters) const {
+ LOG_INFO(Service_HID, "called, deducing the best configuration based on the given parameters!");
+
+ auto& npad =
+ service_manager.GetService<Service::HID::Hid>("hid")
+ ->GetAppletResource()
+ ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+
+ auto& players = Settings::values.players.GetValue();
+
+ const std::size_t min_supported_players =
+ parameters.enable_single_mode ? 1 : parameters.min_players;
+
+ // Disconnect Handheld first.
+ npad.DisconnectNpadAtIndex(8);
+
+ // Deduce the best configuration based on the input parameters.
+ for (std::size_t index = 0; index < players.size() - 2; ++index) {
+ // First, disconnect all controllers regardless of the value of keep_controllers_connected.
+ // This makes it easy to connect the desired controllers.
+ npad.DisconnectNpadAtIndex(index);
+
+ // Only connect the minimum number of required players.
+ if (index >= min_supported_players) {
+ continue;
+ }
+
+ // Connect controllers based on the following priority list from highest to lowest priority:
+ // Pro Controller -> Dual Joycons -> Left Joycon/Right Joycon -> Handheld
+ if (parameters.allow_pro_controller) {
+ npad.AddNewControllerAt(
+ npad.MapSettingsTypeToNPad(Settings::ControllerType::ProController), index);
+ } else if (parameters.allow_dual_joycons) {
+ npad.AddNewControllerAt(
+ npad.MapSettingsTypeToNPad(Settings::ControllerType::DualJoyconDetached), index);
+ } else if (parameters.allow_left_joycon && parameters.allow_right_joycon) {
+ // Assign left joycons to even player indices and right joycons to odd player indices.
+ // We do this since Captain Toad Treasure Tracker expects a left joycon for Player 1 and
+ // a right Joycon for Player 2 in 2 Player Assist mode.
+ if (index % 2 == 0) {
+ npad.AddNewControllerAt(
+ npad.MapSettingsTypeToNPad(Settings::ControllerType::LeftJoycon), index);
+ } else {
+ npad.AddNewControllerAt(
+ npad.MapSettingsTypeToNPad(Settings::ControllerType::RightJoycon), index);
+ }
+ } else if (index == 0 && parameters.enable_single_mode && parameters.allow_handheld &&
+ !Settings::values.use_docked_mode.GetValue()) {
+ // We should *never* reach here under any normal circumstances.
+ npad.AddNewControllerAt(npad.MapSettingsTypeToNPad(Settings::ControllerType::Handheld),
+ index);
+ } else {
+ UNREACHABLE_MSG("Unable to add a new controller based on the given parameters!");
+ }
+ }
+
+ callback();
+}
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/applets/controller.h b/src/core/frontend/applets/controller.h
new file mode 100644
index 000000000..dff71d8d9
--- /dev/null
+++ b/src/core/frontend/applets/controller.h
@@ -0,0 +1,56 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+
+#include "common/common_types.h"
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Core::Frontend {
+
+using BorderColor = std::array<u8, 4>;
+using ExplainText = std::array<char, 0x81>;
+
+struct ControllerParameters {
+ s8 min_players{};
+ s8 max_players{};
+ bool keep_controllers_connected{};
+ bool enable_single_mode{};
+ bool enable_border_color{};
+ std::vector<BorderColor> border_colors{};
+ bool enable_explain_text{};
+ std::vector<ExplainText> explain_text{};
+ bool allow_pro_controller{};
+ bool allow_handheld{};
+ bool allow_dual_joycons{};
+ bool allow_left_joycon{};
+ bool allow_right_joycon{};
+};
+
+class ControllerApplet {
+public:
+ virtual ~ControllerApplet();
+
+ virtual void ReconfigureControllers(std::function<void()> callback,
+ const ControllerParameters& parameters) const = 0;
+};
+
+class DefaultControllerApplet final : public ControllerApplet {
+public:
+ explicit DefaultControllerApplet(Service::SM::ServiceManager& service_manager_);
+ ~DefaultControllerApplet() override;
+
+ void ReconfigureControllers(std::function<void()> callback,
+ const ControllerParameters& parameters) const override;
+
+private:
+ Service::SM::ServiceManager& service_manager;
+};
+
+} // namespace Core::Frontend
diff --git a/src/core/frontend/emu_window.cpp b/src/core/frontend/emu_window.cpp
index 9a081fbd4..8c1193894 100644
--- a/src/core/frontend/emu_window.cpp
+++ b/src/core/frontend/emu_window.cpp
@@ -84,10 +84,12 @@ void EmuWindow::TouchPressed(unsigned framebuffer_x, unsigned framebuffer_y) {
return;
std::lock_guard guard{touch_state->mutex};
- touch_state->touch_x = static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
- (framebuffer_layout.screen.right - framebuffer_layout.screen.left);
- touch_state->touch_y = static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
- (framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
+ touch_state->touch_x =
+ static_cast<float>(framebuffer_x - framebuffer_layout.screen.left) /
+ static_cast<float>(framebuffer_layout.screen.right - framebuffer_layout.screen.left);
+ touch_state->touch_y =
+ static_cast<float>(framebuffer_y - framebuffer_layout.screen.top) /
+ static_cast<float>(framebuffer_layout.screen.bottom - framebuffer_layout.screen.top);
touch_state->touch_pressed = true;
}
diff --git a/src/core/frontend/framebuffer_layout.cpp b/src/core/frontend/framebuffer_layout.cpp
index c1fbc235b..b9a270a55 100644
--- a/src/core/frontend/framebuffer_layout.cpp
+++ b/src/core/frontend/framebuffer_layout.cpp
@@ -14,8 +14,8 @@ namespace Layout {
template <class T>
static Common::Rectangle<T> MaxRectangle(Common::Rectangle<T> window_area,
float screen_aspect_ratio) {
- float scale = std::min(static_cast<float>(window_area.GetWidth()),
- window_area.GetHeight() / screen_aspect_ratio);
+ const float scale = std::min(static_cast<float>(window_area.GetWidth()),
+ static_cast<float>(window_area.GetHeight()) / screen_aspect_ratio);
return Common::Rectangle<T>{0, 0, static_cast<T>(std::round(scale)),
static_cast<T>(std::round(scale * screen_aspect_ratio))};
}
@@ -27,7 +27,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
// so just calculate them both even if the other isn't showing.
FramebufferLayout res{width, height, false, {}};
- const float window_aspect_ratio = static_cast<float>(height) / width;
+ const float window_aspect_ratio = static_cast<float>(height) / static_cast<float>(width);
const float emulation_aspect_ratio = EmulationAspectRatio(
static_cast<AspectRatio>(Settings::values.aspect_ratio.GetValue()), window_aspect_ratio);
@@ -47,7 +47,7 @@ FramebufferLayout DefaultFrameLayout(u32 width, u32 height) {
FramebufferLayout FrameLayoutFromResolutionScale(u32 res_scale) {
u32 width, height;
- if (Settings::values.use_docked_mode) {
+ if (Settings::values.use_docked_mode.GetValue()) {
width = ScreenDocked::Width * res_scale;
height = ScreenDocked::Height * res_scale;
} else {
diff --git a/src/core/frontend/framebuffer_layout.h b/src/core/frontend/framebuffer_layout.h
index 91ecc30ab..e2e3bbbb3 100644
--- a/src/core/frontend/framebuffer_layout.h
+++ b/src/core/frontend/framebuffer_layout.h
@@ -4,6 +4,7 @@
#pragma once
+#include "common/common_types.h"
#include "common/math_util.h"
namespace Layout {
diff --git a/src/core/frontend/input.h b/src/core/frontend/input.h
index 2b098b7c6..25ac5af46 100644
--- a/src/core/frontend/input.h
+++ b/src/core/frontend/input.h
@@ -33,6 +33,9 @@ public:
virtual bool GetAnalogDirectionStatus(AnalogDirection direction) const {
return {};
}
+ virtual bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const {
+ return {};
+ }
};
/// An abstract class template for a factory that can create input devices.
@@ -119,11 +122,18 @@ using ButtonDevice = InputDevice<bool>;
using AnalogDevice = InputDevice<std::tuple<float, float>>;
/**
- * A motion device is an input device that returns a tuple of accelerometer state vector and
- * gyroscope state vector.
+ * A vibration device is an input device that returns an unsigned byte as status.
+ * It represents whether the vibration device supports vibration or not.
+ * If the status returns 1, it supports vibration. Otherwise, it does not support vibration.
+ */
+using VibrationDevice = InputDevice<u8>;
+
+/**
+ * A motion status is an object that returns a tuple of accelerometer state vector,
+ * gyroscope state vector, rotation state vector and orientation state matrix.
*
* For both vectors:
- * x+ is the same direction as LEFT on D-pad.
+ * x+ is the same direction as RIGHT on D-pad.
* y+ is normal to the touch screen, pointing outward.
* z+ is the same direction as UP on D-pad.
*
@@ -133,8 +143,22 @@ using AnalogDevice = InputDevice<std::tuple<float, float>>;
* For gyroscope state vector:
* Orientation is determined by right-hand rule.
* Units: deg/sec
+ *
+ * For rotation state vector
+ * Units: rotations
+ *
+ * For orientation state matrix
+ * x vector
+ * y vector
+ * z vector
+ */
+using MotionStatus = std::tuple<Common::Vec3<float>, Common::Vec3<float>, Common::Vec3<float>,
+ std::array<Common::Vec3f, 3>>;
+
+/**
+ * A motion device is an input device that returns a motion status object
*/
-using MotionDevice = InputDevice<std::tuple<Common::Vec3<float>, Common::Vec3<float>>>;
+using MotionDevice = InputDevice<MotionStatus>;
/**
* A touch device is an input device that returns a tuple of two floats and a bool. The floats are
diff --git a/src/core/gdbstub/gdbstub.cpp b/src/core/gdbstub/gdbstub.cpp
index 79f22a403..97ee65464 100644
--- a/src/core/gdbstub/gdbstub.cpp
+++ b/src/core/gdbstub/gdbstub.cpp
@@ -291,11 +291,11 @@ static void FpuWrite(std::size_t id, u128 val, Kernel::Thread* thread = nullptr)
*/
static u8 HexCharToValue(u8 hex) {
if (hex >= '0' && hex <= '9') {
- return hex - '0';
+ return static_cast<u8>(hex - '0');
} else if (hex >= 'a' && hex <= 'f') {
- return hex - 'a' + 0xA;
+ return static_cast<u8>(hex - 'a' + 0xA);
} else if (hex >= 'A' && hex <= 'F') {
- return hex - 'A' + 0xA;
+ return static_cast<u8>(hex - 'A' + 0xA);
}
LOG_ERROR(Debug_GDBStub, "Invalid nibble: {} ({:02X})", hex, hex);
@@ -310,9 +310,9 @@ static u8 HexCharToValue(u8 hex) {
static u8 NibbleToHex(u8 n) {
n &= 0xF;
if (n < 0xA) {
- return '0' + n;
+ return static_cast<u8>('0' + n);
} else {
- return 'a' + n - 0xA;
+ return static_cast<u8>('a' + n - 0xA);
}
}
@@ -355,8 +355,8 @@ static u64 HexToLong(const u8* src, std::size_t len) {
*/
static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) {
while (len-- > 0) {
- u8 tmp = *src++;
- *dest++ = NibbleToHex(tmp >> 4);
+ const u8 tmp = *src++;
+ *dest++ = NibbleToHex(static_cast<u8>(tmp >> 4));
*dest++ = NibbleToHex(tmp);
}
}
@@ -370,7 +370,7 @@ static void MemToGdbHex(u8* dest, const u8* src, std::size_t len) {
*/
static void GdbHexToMem(u8* dest, const u8* src, std::size_t len) {
while (len-- > 0) {
- *dest++ = (HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]);
+ *dest++ = static_cast<u8>((HexCharToValue(src[0]) << 4) | HexCharToValue(src[1]));
src += 2;
}
}
@@ -602,22 +602,22 @@ static void SendReply(const char* reply) {
memcpy(command_buffer + 1, reply, command_length);
- u8 checksum = CalculateChecksum(command_buffer, command_length + 1);
+ const u8 checksum = CalculateChecksum(command_buffer, command_length + 1);
command_buffer[0] = GDB_STUB_START;
command_buffer[command_length + 1] = GDB_STUB_END;
- command_buffer[command_length + 2] = NibbleToHex(checksum >> 4);
+ command_buffer[command_length + 2] = NibbleToHex(static_cast<u8>(checksum >> 4));
command_buffer[command_length + 3] = NibbleToHex(checksum);
u8* ptr = command_buffer;
u32 left = command_length + 4;
while (left > 0) {
- int sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
+ const auto sent_size = send(gdbserver_socket, reinterpret_cast<char*>(ptr), left, 0);
if (sent_size < 0) {
LOG_ERROR(Debug_GDBStub, "gdb: send failed");
return Shutdown();
}
- left -= sent_size;
+ left -= static_cast<u32>(sent_size);
ptr += sent_size;
}
}
@@ -777,10 +777,10 @@ static void ReadCommand() {
command_buffer[command_length++] = c;
}
- u8 checksum_received = HexCharToValue(ReadByte()) << 4;
- checksum_received |= HexCharToValue(ReadByte());
+ auto checksum_received = static_cast<u32>(HexCharToValue(ReadByte()) << 4);
+ checksum_received |= static_cast<u32>(HexCharToValue(ReadByte()));
- u8 checksum_calculated = CalculateChecksum(command_buffer, command_length);
+ const u32 checksum_calculated = CalculateChecksum(command_buffer, command_length);
if (checksum_received != checksum_calculated) {
LOG_ERROR(Debug_GDBStub,
diff --git a/src/core/hle/ipc_helpers.h b/src/core/hle/ipc_helpers.h
index 1b503331f..d57776ce9 100644
--- a/src/core/hle/ipc_helpers.h
+++ b/src/core/hle/ipc_helpers.h
@@ -12,7 +12,6 @@
#include <utility>
#include "common/assert.h"
#include "common/common_types.h"
-#include "core/core.h"
#include "core/hle/ipc.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
@@ -38,10 +37,11 @@ public:
explicit RequestHelperBase(Kernel::HLERequestContext& context)
: context(&context), cmdbuf(context.CommandBuffer()) {}
- void Skip(unsigned size_in_words, bool set_to_null) {
- if (set_to_null)
+ void Skip(u32 size_in_words, bool set_to_null) {
+ if (set_to_null) {
memset(cmdbuf + index, 0, size_in_words * sizeof(u32));
- index += size_in_words;
+ }
+ index += static_cast<ptrdiff_t>(size_in_words);
}
/**
@@ -49,15 +49,15 @@ public:
*/
void AlignWithPadding() {
if (index & 3) {
- Skip(4 - (index & 3), true);
+ Skip(static_cast<u32>(4 - (index & 3)), true);
}
}
- unsigned GetCurrentOffset() const {
- return static_cast<unsigned>(index);
+ u32 GetCurrentOffset() const {
+ return static_cast<u32>(index);
}
- void SetCurrentOffset(unsigned offset) {
+ void SetCurrentOffset(u32 offset) {
index = static_cast<ptrdiff_t>(offset);
}
};
@@ -72,14 +72,12 @@ public:
AlwaysMoveHandles = 1,
};
- explicit ResponseBuilder(u32* command_buffer) : RequestHelperBase(command_buffer) {}
-
explicit ResponseBuilder(Kernel::HLERequestContext& context, u32 normal_params_size,
u32 num_handles_to_copy = 0, u32 num_objects_to_move = 0,
Flags flags = Flags::None)
-
: RequestHelperBase(context), normal_params_size(normal_params_size),
- num_handles_to_copy(num_handles_to_copy), num_objects_to_move(num_objects_to_move) {
+ num_handles_to_copy(num_handles_to_copy),
+ num_objects_to_move(num_objects_to_move), kernel{context.kernel} {
memset(cmdbuf, 0, sizeof(u32) * IPC::COMMAND_BUFFER_LENGTH);
@@ -89,7 +87,7 @@ public:
// The entire size of the raw data section in u32 units, including the 16 bytes of mandatory
// padding.
- u32 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
+ u64 raw_data_size = sizeof(IPC::DataPayloadHeader) / 4 + 4 + normal_params_size;
u32 num_handles_to_move{};
u32 num_domain_objects{};
@@ -105,7 +103,7 @@ public:
raw_data_size += sizeof(DomainMessageHeader) / 4 + num_domain_objects;
}
- header.data_size.Assign(raw_data_size);
+ header.data_size.Assign(static_cast<u32>(raw_data_size));
if (num_handles_to_copy || num_handles_to_move) {
header.enable_handle_descriptor.Assign(1);
}
@@ -139,7 +137,6 @@ public:
if (context->Session()->IsDomain()) {
context->AddDomainObject(std::move(iface));
} else {
- auto& kernel = Core::System::GetInstance().Kernel();
auto [client, server] = Kernel::Session::Create(kernel, iface->GetServiceName());
context->AddMoveObject(std::move(client));
iface->ClientConnected(std::move(server));
@@ -213,6 +210,7 @@ private:
u32 num_handles_to_copy{};
u32 num_objects_to_move{}; ///< Domain objects or move handles, context dependent
std::ptrdiff_t datapayload_index{};
+ Kernel::KernelCore& kernel;
};
/// Push ///
diff --git a/src/core/hle/kernel/client_session.cpp b/src/core/hle/kernel/client_session.cpp
index 5ab204b9b..be9eba519 100644
--- a/src/core/hle/kernel/client_session.cpp
+++ b/src/core/hle/kernel/client_session.cpp
@@ -48,14 +48,15 @@ ResultVal<std::shared_ptr<ClientSession>> ClientSession::Create(KernelCore& kern
}
ResultCode ClientSession::SendSyncRequest(std::shared_ptr<Thread> thread,
- Core::Memory::Memory& memory) {
+ Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
// Keep ServerSession alive until we're done working with it.
if (!parent->Server()) {
return ERR_SESSION_CLOSED_BY_REMOTE;
}
// Signal the server session that new data is available
- return parent->Server()->HandleSyncRequest(std::move(thread), memory);
+ return parent->Server()->HandleSyncRequest(std::move(thread), memory, core_timing);
}
} // namespace Kernel
diff --git a/src/core/hle/kernel/client_session.h b/src/core/hle/kernel/client_session.h
index c5f760d7d..e5e0690c2 100644
--- a/src/core/hle/kernel/client_session.h
+++ b/src/core/hle/kernel/client_session.h
@@ -16,6 +16,10 @@ namespace Core::Memory {
class Memory;
}
+namespace Core::Timing {
+class CoreTiming;
+}
+
namespace Kernel {
class KernelCore;
@@ -42,7 +46,8 @@ public:
return HANDLE_TYPE;
}
- ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
+ ResultCode SendSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
bool ShouldWait(const Thread* thread) const override;
diff --git a/src/core/hle/kernel/handle_table.cpp b/src/core/hle/kernel/handle_table.cpp
index fb30b6f8b..3e745c18b 100644
--- a/src/core/hle/kernel/handle_table.cpp
+++ b/src/core/hle/kernel/handle_table.cpp
@@ -118,7 +118,7 @@ std::shared_ptr<Object> HandleTable::GetGeneric(Handle handle) const {
void HandleTable::Clear() {
for (u16 i = 0; i < table_size; ++i) {
- generations[i] = i + 1;
+ generations[i] = static_cast<u16>(i + 1);
objects[i] = nullptr;
}
next_free_slot = 0;
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 {
class Memory;
}
+namespace IPC {
+class ResponseBuilder;
+}
+
namespace Service {
class ServiceFrameworkBase;
}
@@ -287,6 +291,8 @@ public:
}
private:
+ friend class IPC::ResponseBuilder;
+
void ParseCommandBuffer(const HandleTable& handle_table, u32_le* src_cmdbuf, bool incoming);
std::array<u32, IPC::COMMAND_BUFFER_LENGTH> cmd_buf;
diff --git a/src/core/hle/kernel/kernel.cpp b/src/core/hle/kernel/kernel.cpp
index cabe8d418..bb3e312a7 100644
--- a/src/core/hle/kernel/kernel.cpp
+++ b/src/core/hle/kernel/kernel.cpp
@@ -7,7 +7,6 @@
#include <bitset>
#include <functional>
#include <memory>
-#include <mutex>
#include <thread>
#include <unordered_map>
#include <utility>
@@ -87,8 +86,6 @@ struct KernelCore::Impl {
}
cores.clear();
- registered_core_threads.reset();
-
process_list.clear();
current_process = nullptr;
@@ -107,7 +104,11 @@ struct KernelCore::Impl {
cores.clear();
exclusive_monitor.reset();
- host_thread_ids.clear();
+
+ num_host_threads = 0;
+ std::fill(register_host_thread_keys.begin(), register_host_thread_keys.end(),
+ std::thread::id{});
+ std::fill(register_host_thread_values.begin(), register_host_thread_values.end(), 0);
}
void InitializePhysicalCores() {
@@ -177,53 +178,58 @@ struct KernelCore::Impl {
void MakeCurrentProcess(Process* process) {
current_process = process;
-
if (process == nullptr) {
return;
}
-
- u32 core_id = GetCurrentHostThreadID();
+ const u32 core_id = GetCurrentHostThreadID();
if (core_id < Core::Hardware::NUM_CPU_CORES) {
system.Memory().SetCurrentPageTable(*process, core_id);
}
}
void RegisterCoreThread(std::size_t core_id) {
- std::unique_lock lock{register_thread_mutex};
+ const std::thread::id this_id = std::this_thread::get_id();
if (!is_multicore) {
- single_core_thread_id = std::this_thread::get_id();
+ single_core_thread_id = this_id;
}
- const std::thread::id this_id = std::this_thread::get_id();
- const auto it = host_thread_ids.find(this_id);
+ const auto end =
+ register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
+ const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
ASSERT(core_id < Core::Hardware::NUM_CPU_CORES);
- ASSERT(it == host_thread_ids.end());
- ASSERT(!registered_core_threads[core_id]);
- host_thread_ids[this_id] = static_cast<u32>(core_id);
- registered_core_threads.set(core_id);
+ ASSERT(it == end);
+ InsertHostThread(static_cast<u32>(core_id));
}
void RegisterHostThread() {
- std::unique_lock lock{register_thread_mutex};
const std::thread::id this_id = std::this_thread::get_id();
- const auto it = host_thread_ids.find(this_id);
- if (it != host_thread_ids.end()) {
- return;
+ const auto end =
+ register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
+ const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
+ if (it == end) {
+ InsertHostThread(registered_thread_ids++);
}
- host_thread_ids[this_id] = registered_thread_ids++;
}
- u32 GetCurrentHostThreadID() const {
+ void InsertHostThread(u32 value) {
+ const size_t index = num_host_threads++;
+ ASSERT_MSG(index < NUM_REGISTRABLE_HOST_THREADS, "Too many host threads");
+ register_host_thread_values[index] = value;
+ register_host_thread_keys[index] = std::this_thread::get_id();
+ }
+
+ [[nodiscard]] u32 GetCurrentHostThreadID() const {
const std::thread::id this_id = std::this_thread::get_id();
- if (!is_multicore) {
- if (single_core_thread_id == this_id) {
- return static_cast<u32>(system.GetCpuManager().CurrentCore());
- }
+ if (!is_multicore && single_core_thread_id == this_id) {
+ return static_cast<u32>(system.GetCpuManager().CurrentCore());
}
- const auto it = host_thread_ids.find(this_id);
- if (it == host_thread_ids.end()) {
+ const auto end =
+ register_host_thread_keys.begin() + static_cast<ptrdiff_t>(num_host_threads);
+ const auto it = std::find(register_host_thread_keys.begin(), end, this_id);
+ if (it == end) {
return Core::INVALID_HOST_THREAD_ID;
}
- return it->second;
+ return register_host_thread_values[static_cast<size_t>(
+ std::distance(register_host_thread_keys.begin(), it))];
}
Core::EmuThreadHandle GetCurrentEmuThreadID() const {
@@ -321,10 +327,14 @@ struct KernelCore::Impl {
std::vector<Kernel::PhysicalCore> cores;
// 0-3 IDs represent core threads, >3 represent others
- std::unordered_map<std::thread::id, u32> host_thread_ids;
- u32 registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
- std::bitset<Core::Hardware::NUM_CPU_CORES> registered_core_threads;
- std::mutex register_thread_mutex;
+ std::atomic<u32> registered_thread_ids{Core::Hardware::NUM_CPU_CORES};
+
+ // Number of host threads is a relatively high number to avoid overflowing
+ static constexpr size_t NUM_REGISTRABLE_HOST_THREADS = 64;
+ std::atomic<size_t> num_host_threads{0};
+ std::array<std::atomic<std::thread::id>, NUM_REGISTRABLE_HOST_THREADS>
+ register_host_thread_keys{};
+ std::array<std::atomic<u32>, NUM_REGISTRABLE_HOST_THREADS> register_host_thread_values{};
// Kernel memory management
std::unique_ptr<Memory::MemoryManager> memory_manager;
diff --git a/src/core/hle/kernel/physical_core.cpp b/src/core/hle/kernel/physical_core.cpp
index c6bbdb080..6e04d025f 100644
--- a/src/core/hle/kernel/physical_core.cpp
+++ b/src/core/hle/kernel/physical_core.cpp
@@ -2,30 +2,18 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include "common/assert.h"
-#include "common/logging/log.h"
#include "common/spin_lock.h"
-#include "core/arm/arm_interface.h"
-#ifdef ARCHITECTURE_x86_64
-#include "core/arm/dynarmic/arm_dynarmic_32.h"
-#include "core/arm/dynarmic/arm_dynarmic_64.h"
-#endif
#include "core/arm/cpu_interrupt_handler.h"
-#include "core/arm/exclusive_monitor.h"
-#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core.h"
#include "core/hle/kernel/physical_core.h"
#include "core/hle/kernel/scheduler.h"
-#include "core/hle/kernel/thread.h"
namespace Kernel {
PhysicalCore::PhysicalCore(Core::System& system, std::size_t id, Kernel::Scheduler& scheduler,
Core::CPUInterruptHandler& interrupt_handler)
- : interrupt_handler{interrupt_handler}, core_index{id}, scheduler{scheduler} {
-
- guard = std::make_unique<Common::SpinLock>();
-}
+ : interrupt_handler{interrupt_handler},
+ core_index{id}, scheduler{scheduler}, guard{std::make_unique<Common::SpinLock>()} {}
PhysicalCore::~PhysicalCore() = default;
diff --git a/src/core/hle/kernel/process.cpp b/src/core/hle/kernel/process.cpp
index ff9d9248b..b17529dee 100644
--- a/src/core/hle/kernel/process.cpp
+++ b/src/core/hle/kernel/process.cpp
@@ -4,6 +4,7 @@
#include <algorithm>
#include <bitset>
+#include <ctime>
#include <memory>
#include <random>
#include "common/alignment.h"
@@ -123,7 +124,7 @@ std::shared_ptr<Process> Process::Create(Core::System& system, std::string name,
: kernel.CreateNewUserProcessID();
process->capabilities.InitializeForMetadatalessProcess();
- std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(0));
+ std::mt19937 rng(Settings::values.rng_seed.GetValue().value_or(std::time(nullptr)));
std::uniform_int_distribution<u64> distribution;
std::generate(process->random_entropy.begin(), process->random_entropy.end(),
[&] { return distribution(rng); });
diff --git a/src/core/hle/kernel/scheduler.cpp b/src/core/hle/kernel/scheduler.cpp
index a4b234424..6b7db5372 100644
--- a/src/core/hle/kernel/scheduler.cpp
+++ b/src/core/hle/kernel/scheduler.cpp
@@ -72,7 +72,7 @@ u32 GlobalScheduler::SelectThreads() {
if (top_thread != nullptr) {
// TODO(Blinkhawk): Implement Thread Pinning
} else {
- idle_cores |= (1ul << core);
+ idle_cores |= (1U << core);
}
top_threads[core] = top_thread;
}
@@ -126,7 +126,7 @@ u32 GlobalScheduler::SelectThreads() {
top_threads[core_id] = suggested;
}
- idle_cores &= ~(1ul << core_id);
+ idle_cores &= ~(1U << core_id);
}
u32 cores_needing_context_switch{};
for (u32 core = 0; core < Core::Hardware::NUM_CPU_CORES; core++) {
@@ -134,7 +134,7 @@ u32 GlobalScheduler::SelectThreads() {
ASSERT(top_threads[core] == nullptr ||
static_cast<u32>(top_threads[core]->GetProcessorID()) == core);
if (update_thread(top_threads[core], sched)) {
- cores_needing_context_switch |= (1ul << core);
+ cores_needing_context_switch |= (1U << core);
}
}
return cores_needing_context_switch;
@@ -364,7 +364,7 @@ void GlobalScheduler::EnableInterruptAndSchedule(u32 cores_pending_reschedule,
} else {
must_context_switch = true;
}
- cores_pending_reschedule &= ~(1ul << core);
+ cores_pending_reschedule &= ~(1U << core);
}
if (must_context_switch) {
auto& core_scheduler = kernel.CurrentScheduler();
@@ -756,14 +756,18 @@ void Scheduler::SwitchToCurrent() {
current_thread = selected_thread;
is_context_switch_pending = false;
}
- while (!is_context_switch_pending) {
+ const auto is_switch_pending = [this] {
+ std::scoped_lock lock{guard};
+ return is_context_switch_pending;
+ };
+ do {
if (current_thread != nullptr && !current_thread->IsHLEThread()) {
current_thread->context_guard.lock();
if (!current_thread->IsRunnable()) {
current_thread->context_guard.unlock();
break;
}
- if (current_thread->GetProcessorID() != core_id) {
+ if (static_cast<u32>(current_thread->GetProcessorID()) != core_id) {
current_thread->context_guard.unlock();
break;
}
@@ -775,7 +779,7 @@ void Scheduler::SwitchToCurrent() {
next_context = &idle_thread->GetHostContext();
}
Common::Fiber::YieldTo(switch_fiber, *next_context);
- }
+ } while (!is_switch_pending());
}
}
diff --git a/src/core/hle/kernel/scheduler.h b/src/core/hle/kernel/scheduler.h
index 36e3c26fb..b6f04dcea 100644
--- a/src/core/hle/kernel/scheduler.h
+++ b/src/core/hle/kernel/scheduler.h
@@ -188,7 +188,7 @@ private:
/// Scheduler lock mechanisms.
bool is_locked{};
- Common::SpinLock inner_lock{};
+ std::mutex inner_lock;
std::atomic<s64> scope_lock{};
Core::EmuThreadHandle current_owner{Core::EmuThreadHandle::InvalidHandle()};
diff --git a/src/core/hle/kernel/server_session.cpp b/src/core/hle/kernel/server_session.cpp
index 7e6391c6c..8c19f2534 100644
--- a/src/core/hle/kernel/server_session.cpp
+++ b/src/core/hle/kernel/server_session.cpp
@@ -8,7 +8,6 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/logging/log.h"
-#include "core/core.h"
#include "core/core_timing.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
@@ -185,10 +184,11 @@ ResultCode ServerSession::CompleteSyncRequest() {
}
ResultCode ServerSession::HandleSyncRequest(std::shared_ptr<Thread> thread,
- Core::Memory::Memory& memory) {
+ Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing) {
const ResultCode result = QueueSyncRequest(std::move(thread), memory);
const auto delay = std::chrono::nanoseconds{kernel.IsMulticore() ? 0 : 20000};
- Core::System::GetInstance().CoreTiming().ScheduleEvent(delay, request_event, {});
+ core_timing.ScheduleEvent(delay, request_event, {});
return result;
}
diff --git a/src/core/hle/kernel/server_session.h b/src/core/hle/kernel/server_session.h
index 403aaf10b..d23e9ec68 100644
--- a/src/core/hle/kernel/server_session.h
+++ b/src/core/hle/kernel/server_session.h
@@ -18,8 +18,9 @@ class Memory;
}
namespace Core::Timing {
+class CoreTiming;
struct EventType;
-}
+} // namespace Core::Timing
namespace Kernel {
@@ -87,12 +88,14 @@ public:
/**
* Handle a sync request from the emulated application.
*
- * @param thread Thread that initiated the request.
- * @param memory Memory context to handle the sync request under.
+ * @param thread Thread that initiated the request.
+ * @param memory Memory context to handle the sync request under.
+ * @param core_timing Core timing context to schedule the request event under.
*
* @returns ResultCode from the operation.
*/
- ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory);
+ ResultCode HandleSyncRequest(std::shared_ptr<Thread> thread, Core::Memory::Memory& memory,
+ Core::Timing::CoreTiming& core_timing);
bool ShouldWait(const Thread* thread) const override;
diff --git a/src/core/hle/kernel/svc.cpp b/src/core/hle/kernel/svc.cpp
index 01ae57053..bafd1ced7 100644
--- a/src/core/hle/kernel/svc.cpp
+++ b/src/core/hle/kernel/svc.cpp
@@ -346,7 +346,7 @@ static ResultCode SendSyncRequest(Core::System& system, Handle handle) {
SchedulerLock lock(system.Kernel());
thread->InvalidateHLECallback();
thread->SetStatus(ThreadStatus::WaitIPC);
- session->SendSyncRequest(SharedFrom(thread), system.Memory());
+ session->SendSyncRequest(SharedFrom(thread), system.Memory(), system.CoreTiming());
}
if (thread->HasHLECallback()) {
diff --git a/src/core/hle/kernel/thread.cpp b/src/core/hle/kernel/thread.cpp
index d132aba34..da0cb26b6 100644
--- a/src/core/hle/kernel/thread.cpp
+++ b/src/core/hle/kernel/thread.cpp
@@ -13,7 +13,6 @@
#include "common/logging/log.h"
#include "common/thread_queue_list.h"
#include "core/arm/arm_interface.h"
-#include "core/arm/unicorn/arm_unicorn.h"
#include "core/core.h"
#include "core/cpu_manager.h"
#include "core/hardware_properties.h"
@@ -217,8 +216,7 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
} else {
thread->tls_address = 0;
}
- // TODO(peachum): move to ScheduleThread() when scheduler is added so selected core is used
- // to initialize the context
+
thread->arm_interface.reset();
if ((type_flags & THREADTYPE_HLE) == 0) {
#ifdef ARCHITECTURE_x86_64
@@ -231,19 +229,10 @@ ResultVal<std::shared_ptr<Thread>> Thread::Create(Core::System& system, ThreadTy
system, kernel.Interrupts(), kernel.IsMulticore(), kernel.GetExclusiveMonitor(),
processor_id);
}
-
#else
- if (owner_process && !owner_process->Is64BitProcess()) {
- thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
- system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch32,
- processor_id);
- } else {
- thread->arm_interface = std::make_shared<Core::ARM_Unicorn>(
- system, kernel.Interrupts(), kernel.IsMulticore(), ARM_Unicorn::Arch::AArch64,
- processor_id);
- }
- LOG_WARNING(Core, "CPU JIT requested, but Dynarmic not available");
+#error Platform not supported yet.
#endif
+
ResetThreadContext32(thread->context_32, static_cast<u32>(stack_top),
static_cast<u32>(entry_point), static_cast<u32>(arg));
ResetThreadContext64(thread->context_64, stack_top, entry_point, arg);
diff --git a/src/core/hle/service/acc/acc.cpp b/src/core/hle/service/acc/acc.cpp
index eb54cb123..c2c11dbcb 100644
--- a/src/core/hle/service/acc/acc.cpp
+++ b/src/core/hle/service/acc/acc.cpp
@@ -11,6 +11,7 @@
#include "common/string_util.h"
#include "common/swap.h"
#include "core/constants.h"
+#include "core/core.h"
#include "core/core_timing.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
@@ -496,7 +497,7 @@ public:
{3, nullptr, "LoadIdTokenCache"},
{130, nullptr, "GetNintendoAccountUserResourceCacheForApplication"},
{150, nullptr, "CreateAuthorizationRequest"},
- {160, nullptr, "StoreOpenContext"},
+ {160, &IManagerForApplication::StoreOpenContext, "StoreOpenContext"},
{170, nullptr, "LoadNetworkServiceLicenseKindAsync"},
};
// clang-format on
@@ -520,6 +521,12 @@ private:
rb.PushRaw<u64>(user_id.GetNintendoID());
}
+ void StoreOpenContext(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
Common::UUID user_id;
};
@@ -735,8 +742,10 @@ void Module::Interface::IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx
bool is_locked = false;
if (res != Loader::ResultStatus::Success) {
- FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
- auto nacp_unique = pm.GetControlMetadata().first;
+ const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
+ const auto nacp_unique = pm.GetControlMetadata().first;
if (nacp_unique != nullptr) {
is_locked = nacp_unique->GetUserAccountSwitchLock();
@@ -774,6 +783,17 @@ void Module::Interface::ListQualifiedUsers(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
}
+void Module::Interface::LoadOpenContext(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_ACC, "(STUBBED) called");
+
+ // This is similar to GetBaasAccountManagerForApplication
+ // This command is used concurrently with ListOpenContextStoredUsers
+ // TODO: Find the differences between this and GetBaasAccountManagerForApplication
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IManagerForApplication>(profile_manager->GetLastOpenedUser());
+}
+
void Module::Interface::ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_ACC, "(STUBBED) called");
diff --git a/src/core/hle/service/acc/acc.h b/src/core/hle/service/acc/acc.h
index d4c6395c6..c611efd89 100644
--- a/src/core/hle/service/acc/acc.h
+++ b/src/core/hle/service/acc/acc.h
@@ -34,6 +34,7 @@ public:
void IsUserAccountSwitchLocked(Kernel::HLERequestContext& ctx);
void GetProfileEditor(Kernel::HLERequestContext& ctx);
void ListQualifiedUsers(Kernel::HLERequestContext& ctx);
+ void LoadOpenContext(Kernel::HLERequestContext& ctx);
void ListOpenContextStoredUsers(Kernel::HLERequestContext& ctx);
private:
diff --git a/src/core/hle/service/acc/acc_u0.cpp b/src/core/hle/service/acc/acc_u0.cpp
index cb44e06b7..75a24f8f5 100644
--- a/src/core/hle/service/acc/acc_u0.cpp
+++ b/src/core/hle/service/acc/acc_u0.cpp
@@ -29,7 +29,7 @@ ACC_U0::ACC_U0(std::shared_ptr<Module> module, std::shared_ptr<ProfileManager> p
{110, nullptr, "StoreSaveDataThumbnail"},
{111, nullptr, "ClearSaveDataThumbnail"},
{120, nullptr, "CreateGuestLoginRequest"},
- {130, nullptr, "LoadOpenContext"}, // 5.0.0+
+ {130, &ACC_U0::LoadOpenContext, "LoadOpenContext"}, // 5.0.0+
{131, &ACC_U0::ListOpenContextStoredUsers, "ListOpenContextStoredUsers"}, // 6.0.0+
{140, &ACC_U0::InitializeApplicationInfoRestricted, "InitializeApplicationInfoRestricted"}, // 6.0.0+
{141, &ACC_U0::ListQualifiedUsers, "ListQualifiedUsers"}, // 6.0.0+
diff --git a/src/core/hle/service/am/am.cpp b/src/core/hle/service/am/am.cpp
index 7d92b25a3..63421b963 100644
--- a/src/core/hle/service/am/am.cpp
+++ b/src/core/hle/service/am/am.cpp
@@ -751,7 +751,7 @@ void ICommonStateGetter::GetDefaultDisplayResolution(Kernel::HLERequestContext&
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- if (Settings::values.use_docked_mode) {
+ if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
@@ -824,7 +824,7 @@ void IStorage::Open(Kernel::HLERequestContext& ctx) {
}
void ICommonStateGetter::GetOperationMode(Kernel::HLERequestContext& ctx) {
- const bool use_docked_mode{Settings::values.use_docked_mode};
+ const bool use_docked_mode{Settings::values.use_docked_mode.GetValue()};
LOG_DEBUG(Service_AM, "called, use_docked_mode={}", use_docked_mode);
IPC::ResponseBuilder rb{ctx, 3};
@@ -1192,7 +1192,7 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{120, nullptr, "ExecuteProgram"},
{121, nullptr, "ClearUserChannel"},
{122, nullptr, "UnpopToUserChannel"},
- {123, nullptr, "GetPreviousProgramIndex"},
+ {123, &IApplicationFunctions::GetPreviousProgramIndex, "GetPreviousProgramIndex"},
{124, nullptr, "EnableApplicationAllThreadDumpOnCrash"},
{130, &IApplicationFunctions::GetGpuErrorDetectedSystemEvent, "GetGpuErrorDetectedSystemEvent"},
{140, &IApplicationFunctions::GetFriendInvitationStorageChannelEvent, "GetFriendInvitationStorageChannelEvent"},
@@ -1201,6 +1201,8 @@ IApplicationFunctions::IApplicationFunctions(Core::System& system_)
{151, nullptr, "TryPopFromNotificationStorageChannel"},
{160, nullptr, "GetHealthWarningDisappearedSystemEvent"},
{170, nullptr, "SetHdcpAuthenticationActivated"},
+ {180, nullptr, "GetLaunchRequiredVersion"},
+ {181, nullptr, "UpgradeLaunchRequiredVersion"},
{500, nullptr, "StartContinuousRecordingFlushForDebug"},
{1000, nullptr, "CreateMovieMaker"},
{1001, nullptr, "PrepareForJit"},
@@ -1379,13 +1381,16 @@ void IApplicationFunctions::GetDisplayVersion(Kernel::HLERequestContext& ctx) {
const auto res = [this] {
const auto title_id = system.CurrentProcess()->GetTitleID();
- FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
auto res = pm.GetControlMetadata();
if (res.first != nullptr) {
return res;
}
- FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
+ const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
@@ -1413,13 +1418,16 @@ void IApplicationFunctions::GetDesiredLanguage(Kernel::HLERequestContext& ctx) {
const auto res = [this] {
const auto title_id = system.CurrentProcess()->GetTitleID();
- FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
auto res = pm.GetControlMetadata();
if (res.first != nullptr) {
return res;
}
- FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id)};
+ const FileSys::PatchManager pm_update{FileSys::GetUpdateTitleID(title_id),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
return pm_update.GetControlMetadata();
}();
@@ -1554,6 +1562,14 @@ void IApplicationFunctions::QueryApplicationPlayStatisticsByUid(Kernel::HLEReque
rb.Push<u32>(0);
}
+void IApplicationFunctions::GetPreviousProgramIndex(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_AM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(previous_program_index);
+}
+
void IApplicationFunctions::GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_AM, "(STUBBED) called");
diff --git a/src/core/hle/service/am/am.h b/src/core/hle/service/am/am.h
index 6e69796ec..bcc06affe 100644
--- a/src/core/hle/service/am/am.h
+++ b/src/core/hle/service/am/am.h
@@ -288,11 +288,13 @@ private:
void SetApplicationCopyrightVisibility(Kernel::HLERequestContext& ctx);
void QueryApplicationPlayStatistics(Kernel::HLERequestContext& ctx);
void QueryApplicationPlayStatisticsByUid(Kernel::HLERequestContext& ctx);
+ void GetPreviousProgramIndex(Kernel::HLERequestContext& ctx);
void GetGpuErrorDetectedSystemEvent(Kernel::HLERequestContext& ctx);
void GetFriendInvitationStorageChannelEvent(Kernel::HLERequestContext& ctx);
bool launch_popped_application_specific = false;
bool launch_popped_account_preselect = false;
+ s32 previous_program_index{-1};
Kernel::EventPair gpu_error_detected_event;
Kernel::EventPair friend_invitation_storage_channel_event;
Core::System& system;
diff --git a/src/core/hle/service/am/applet_ae.cpp b/src/core/hle/service/am/applet_ae.cpp
index 9df286d17..be23ca747 100644
--- a/src/core/hle/service/am/applet_ae.cpp
+++ b/src/core/hle/service/am/applet_ae.cpp
@@ -3,8 +3,8 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
-#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/nvflinger/nvflinger.h"
diff --git a/src/core/hle/service/am/applets/applets.cpp b/src/core/hle/service/am/applets/applets.cpp
index c3261f3e6..2b626bb40 100644
--- a/src/core/hle/service/am/applets/applets.cpp
+++ b/src/core/hle/service/am/applets/applets.cpp
@@ -5,6 +5,7 @@
#include <cstring>
#include "common/assert.h"
#include "core/core.h"
+#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/error.h"
#include "core/frontend/applets/general_frontend.h"
#include "core/frontend/applets/profile_select.h"
@@ -15,6 +16,7 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applets/applets.h"
+#include "core/hle/service/am/applets/controller.h"
#include "core/hle/service/am/applets/error.h"
#include "core/hle/service/am/applets/general_backend.h"
#include "core/hle/service/am/applets/profile_select.h"
@@ -140,14 +142,14 @@ void Applet::Initialize() {
AppletFrontendSet::AppletFrontendSet() = default;
-AppletFrontendSet::AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
+AppletFrontendSet::AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce,
+ ErrorApplet error, ParentalControlsApplet parental_controls,
PhotoViewer photo_viewer, ProfileSelect profile_select,
- SoftwareKeyboard software_keyboard, WebBrowser web_browser,
- ECommerceApplet e_commerce)
- : parental_controls{std::move(parental_controls)}, error{std::move(error)},
- photo_viewer{std::move(photo_viewer)}, profile_select{std::move(profile_select)},
- software_keyboard{std::move(software_keyboard)}, web_browser{std::move(web_browser)},
- e_commerce{std::move(e_commerce)} {}
+ SoftwareKeyboard software_keyboard, WebBrowser web_browser)
+ : controller{std::move(controller)}, e_commerce{std::move(e_commerce)}, error{std::move(error)},
+ parental_controls{std::move(parental_controls)}, photo_viewer{std::move(photo_viewer)},
+ profile_select{std::move(profile_select)}, software_keyboard{std::move(software_keyboard)},
+ web_browser{std::move(web_browser)} {}
AppletFrontendSet::~AppletFrontendSet() = default;
@@ -164,20 +166,37 @@ const AppletFrontendSet& AppletManager::GetAppletFrontendSet() const {
}
void AppletManager::SetAppletFrontendSet(AppletFrontendSet set) {
- if (set.parental_controls != nullptr)
- frontend.parental_controls = std::move(set.parental_controls);
- if (set.error != nullptr)
+ if (set.controller != nullptr) {
+ frontend.controller = std::move(set.controller);
+ }
+
+ if (set.e_commerce != nullptr) {
+ frontend.e_commerce = std::move(set.e_commerce);
+ }
+
+ if (set.error != nullptr) {
frontend.error = std::move(set.error);
- if (set.photo_viewer != nullptr)
+ }
+
+ if (set.parental_controls != nullptr) {
+ frontend.parental_controls = std::move(set.parental_controls);
+ }
+
+ if (set.photo_viewer != nullptr) {
frontend.photo_viewer = std::move(set.photo_viewer);
- if (set.profile_select != nullptr)
+ }
+
+ if (set.profile_select != nullptr) {
frontend.profile_select = std::move(set.profile_select);
- if (set.software_keyboard != nullptr)
+ }
+
+ if (set.software_keyboard != nullptr) {
frontend.software_keyboard = std::move(set.software_keyboard);
- if (set.web_browser != nullptr)
+ }
+
+ if (set.web_browser != nullptr) {
frontend.web_browser = std::move(set.web_browser);
- if (set.e_commerce != nullptr)
- frontend.e_commerce = std::move(set.e_commerce);
+ }
}
void AppletManager::SetDefaultAppletFrontendSet() {
@@ -186,15 +205,24 @@ void AppletManager::SetDefaultAppletFrontendSet() {
}
void AppletManager::SetDefaultAppletsIfMissing() {
- if (frontend.parental_controls == nullptr) {
- frontend.parental_controls =
- std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
+ if (frontend.controller == nullptr) {
+ frontend.controller =
+ std::make_unique<Core::Frontend::DefaultControllerApplet>(system.ServiceManager());
+ }
+
+ if (frontend.e_commerce == nullptr) {
+ frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
}
if (frontend.error == nullptr) {
frontend.error = std::make_unique<Core::Frontend::DefaultErrorApplet>();
}
+ if (frontend.parental_controls == nullptr) {
+ frontend.parental_controls =
+ std::make_unique<Core::Frontend::DefaultParentalControlsApplet>();
+ }
+
if (frontend.photo_viewer == nullptr) {
frontend.photo_viewer = std::make_unique<Core::Frontend::DefaultPhotoViewerApplet>();
}
@@ -211,10 +239,6 @@ void AppletManager::SetDefaultAppletsIfMissing() {
if (frontend.web_browser == nullptr) {
frontend.web_browser = std::make_unique<Core::Frontend::DefaultWebBrowserApplet>();
}
-
- if (frontend.e_commerce == nullptr) {
- frontend.e_commerce = std::make_unique<Core::Frontend::DefaultECommerceApplet>();
- }
}
void AppletManager::ClearAll() {
@@ -225,6 +249,8 @@ std::shared_ptr<Applet> AppletManager::GetApplet(AppletId id) const {
switch (id) {
case AppletId::Auth:
return std::make_shared<Auth>(system, *frontend.parental_controls);
+ case AppletId::Controller:
+ return std::make_shared<Controller>(system, *frontend.controller);
case AppletId::Error:
return std::make_shared<Error>(system, *frontend.error);
case AppletId::ProfileSelect:
diff --git a/src/core/hle/service/am/applets/applets.h b/src/core/hle/service/am/applets/applets.h
index e75be86a2..a1f4cf897 100644
--- a/src/core/hle/service/am/applets/applets.h
+++ b/src/core/hle/service/am/applets/applets.h
@@ -17,6 +17,7 @@ class System;
}
namespace Core::Frontend {
+class ControllerApplet;
class ECommerceApplet;
class ErrorApplet;
class ParentalControlsApplet;
@@ -155,19 +156,20 @@ protected:
};
struct AppletFrontendSet {
- using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
+ using ControllerApplet = std::unique_ptr<Core::Frontend::ControllerApplet>;
+ using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
using ErrorApplet = std::unique_ptr<Core::Frontend::ErrorApplet>;
+ using ParentalControlsApplet = std::unique_ptr<Core::Frontend::ParentalControlsApplet>;
using PhotoViewer = std::unique_ptr<Core::Frontend::PhotoViewerApplet>;
using ProfileSelect = std::unique_ptr<Core::Frontend::ProfileSelectApplet>;
using SoftwareKeyboard = std::unique_ptr<Core::Frontend::SoftwareKeyboardApplet>;
using WebBrowser = std::unique_ptr<Core::Frontend::WebBrowserApplet>;
- using ECommerceApplet = std::unique_ptr<Core::Frontend::ECommerceApplet>;
AppletFrontendSet();
- AppletFrontendSet(ParentalControlsApplet parental_controls, ErrorApplet error,
- PhotoViewer photo_viewer, ProfileSelect profile_select,
- SoftwareKeyboard software_keyboard, WebBrowser web_browser,
- ECommerceApplet e_commerce);
+ AppletFrontendSet(ControllerApplet controller, ECommerceApplet e_commerce, ErrorApplet error,
+ ParentalControlsApplet parental_controls, PhotoViewer photo_viewer,
+ ProfileSelect profile_select, SoftwareKeyboard software_keyboard,
+ WebBrowser web_browser);
~AppletFrontendSet();
AppletFrontendSet(const AppletFrontendSet&) = delete;
@@ -176,13 +178,14 @@ struct AppletFrontendSet {
AppletFrontendSet(AppletFrontendSet&&) noexcept;
AppletFrontendSet& operator=(AppletFrontendSet&&) noexcept;
- ParentalControlsApplet parental_controls;
+ ControllerApplet controller;
+ ECommerceApplet e_commerce;
ErrorApplet error;
+ ParentalControlsApplet parental_controls;
PhotoViewer photo_viewer;
ProfileSelect profile_select;
SoftwareKeyboard software_keyboard;
WebBrowser web_browser;
- ECommerceApplet e_commerce;
};
class AppletManager {
diff --git a/src/core/hle/service/am/applets/controller.cpp b/src/core/hle/service/am/applets/controller.cpp
new file mode 100644
index 000000000..3ca63f020
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.cpp
@@ -0,0 +1,252 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstring>
+
+#include "common/assert.h"
+#include "common/logging/log.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/frontend/applets/controller.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/am.h"
+#include "core/hle/service/am/applets/controller.h"
+#include "core/hle/service/hid/controllers/npad.h"
+
+namespace Service::AM::Applets {
+
+// This error code (0x183ACA) is thrown when the applet fails to initialize.
+[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3101{ErrorModule::HID, 3101};
+// This error code (0x183CCA) is thrown when the u32 result in ControllerSupportResultInfo is 2.
+[[maybe_unused]] constexpr ResultCode ERR_CONTROLLER_APPLET_3102{ErrorModule::HID, 3102};
+
+static Core::Frontend::ControllerParameters ConvertToFrontendParameters(
+ ControllerSupportArgPrivate private_arg, ControllerSupportArgHeader header, bool enable_text,
+ std::vector<IdentificationColor> identification_colors, std::vector<ExplainText> text) {
+ HID::Controller_NPad::NpadStyleSet npad_style_set;
+ npad_style_set.raw = private_arg.style_set;
+
+ return {
+ .min_players = std::max(s8(1), header.player_count_min),
+ .max_players = header.player_count_max,
+ .keep_controllers_connected = header.enable_take_over_connection,
+ .enable_single_mode = header.enable_single_mode,
+ .enable_border_color = header.enable_identification_color,
+ .border_colors = identification_colors,
+ .enable_explain_text = enable_text,
+ .explain_text = text,
+ .allow_pro_controller = npad_style_set.pro_controller == 1,
+ .allow_handheld = npad_style_set.handheld == 1,
+ .allow_dual_joycons = npad_style_set.joycon_dual == 1,
+ .allow_left_joycon = npad_style_set.joycon_left == 1,
+ .allow_right_joycon = npad_style_set.joycon_right == 1,
+ };
+}
+
+Controller::Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_)
+ : Applet{system_.Kernel()}, frontend(frontend_) {}
+
+Controller::~Controller() = default;
+
+void Controller::Initialize() {
+ Applet::Initialize();
+
+ LOG_INFO(Service_HID, "Initializing Controller Applet.");
+
+ LOG_DEBUG(Service_HID,
+ "Initializing Applet with common_args: arg_version={}, lib_version={}, "
+ "play_startup_sound={}, size={}, system_tick={}, theme_color={}",
+ common_args.arguments_version, common_args.library_version,
+ common_args.play_startup_sound, common_args.size, common_args.system_tick,
+ common_args.theme_color);
+
+ controller_applet_version = ControllerAppletVersion{common_args.library_version};
+
+ const auto private_arg_storage = broker.PopNormalDataToApplet();
+ ASSERT(private_arg_storage != nullptr);
+
+ const auto& private_arg = private_arg_storage->GetData();
+ ASSERT(private_arg.size() == sizeof(ControllerSupportArgPrivate));
+
+ std::memcpy(&controller_private_arg, private_arg.data(), private_arg.size());
+ ASSERT_MSG(controller_private_arg.arg_private_size == sizeof(ControllerSupportArgPrivate),
+ "Unknown ControllerSupportArgPrivate revision={} with size={}",
+ controller_applet_version, controller_private_arg.arg_private_size);
+
+ // Some games such as Cave Story+ set invalid values for the ControllerSupportMode.
+ // Defer to arg_size to set the ControllerSupportMode.
+ if (controller_private_arg.mode >= ControllerSupportMode::MaxControllerSupportMode) {
+ switch (controller_private_arg.arg_size) {
+ case sizeof(ControllerSupportArgOld):
+ case sizeof(ControllerSupportArgNew):
+ controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
+ break;
+ case sizeof(ControllerUpdateFirmwareArg):
+ controller_private_arg.mode = ControllerSupportMode::ShowControllerFirmwareUpdate;
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown ControllerPrivateArg mode={} with arg_size={}",
+ controller_private_arg.mode, controller_private_arg.arg_size);
+ controller_private_arg.mode = ControllerSupportMode::ShowControllerSupport;
+ break;
+ }
+ }
+
+ // Some games such as Cave Story+ set invalid values for the ControllerSupportCaller.
+ // This is always 0 (Application) except with ShowControllerFirmwareUpdateForSystem.
+ if (controller_private_arg.caller >= ControllerSupportCaller::MaxControllerSupportCaller) {
+ if (controller_private_arg.flag_1 &&
+ controller_private_arg.mode == ControllerSupportMode::ShowControllerFirmwareUpdate) {
+ controller_private_arg.caller = ControllerSupportCaller::System;
+ } else {
+ controller_private_arg.caller = ControllerSupportCaller::Application;
+ }
+ }
+
+ switch (controller_private_arg.mode) {
+ case ControllerSupportMode::ShowControllerSupport:
+ case ControllerSupportMode::ShowControllerStrapGuide: {
+ const auto user_arg_storage = broker.PopNormalDataToApplet();
+ ASSERT(user_arg_storage != nullptr);
+
+ const auto& user_arg = user_arg_storage->GetData();
+ switch (controller_applet_version) {
+ case ControllerAppletVersion::Version3:
+ case ControllerAppletVersion::Version4:
+ case ControllerAppletVersion::Version5:
+ ASSERT(user_arg.size() == sizeof(ControllerSupportArgOld));
+ std::memcpy(&controller_user_arg_old, user_arg.data(), user_arg.size());
+ break;
+ case ControllerAppletVersion::Version7:
+ ASSERT(user_arg.size() == sizeof(ControllerSupportArgNew));
+ std::memcpy(&controller_user_arg_new, user_arg.data(), user_arg.size());
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown ControllerSupportArg revision={} with size={}",
+ controller_applet_version, controller_private_arg.arg_size);
+ ASSERT(user_arg.size() >= sizeof(ControllerSupportArgNew));
+ std::memcpy(&controller_user_arg_new, user_arg.data(), sizeof(ControllerSupportArgNew));
+ break;
+ }
+ break;
+ }
+ case ControllerSupportMode::ShowControllerFirmwareUpdate: {
+ const auto update_arg_storage = broker.PopNormalDataToApplet();
+ ASSERT(update_arg_storage != nullptr);
+
+ const auto& update_arg = update_arg_storage->GetData();
+ ASSERT(update_arg.size() == sizeof(ControllerUpdateFirmwareArg));
+
+ std::memcpy(&controller_update_arg, update_arg.data(), update_arg.size());
+ break;
+ }
+ default: {
+ UNIMPLEMENTED_MSG("Unimplemented ControllerSupportMode={}", controller_private_arg.mode);
+ break;
+ }
+ }
+}
+
+bool Controller::TransactionComplete() const {
+ return complete;
+}
+
+ResultCode Controller::GetStatus() const {
+ return status;
+}
+
+void Controller::ExecuteInteractive() {
+ UNREACHABLE_MSG("Attempted to call interactive execution on non-interactive applet.");
+}
+
+void Controller::Execute() {
+ switch (controller_private_arg.mode) {
+ case ControllerSupportMode::ShowControllerSupport: {
+ const auto parameters = [this] {
+ switch (controller_applet_version) {
+ case ControllerAppletVersion::Version3:
+ case ControllerAppletVersion::Version4:
+ case ControllerAppletVersion::Version5:
+ return ConvertToFrontendParameters(
+ controller_private_arg, controller_user_arg_old.header,
+ controller_user_arg_old.enable_explain_text,
+ std::vector<IdentificationColor>(
+ controller_user_arg_old.identification_colors.begin(),
+ controller_user_arg_old.identification_colors.end()),
+ std::vector<ExplainText>(controller_user_arg_old.explain_text.begin(),
+ controller_user_arg_old.explain_text.end()));
+ case ControllerAppletVersion::Version7:
+ default:
+ return ConvertToFrontendParameters(
+ controller_private_arg, controller_user_arg_new.header,
+ controller_user_arg_new.enable_explain_text,
+ std::vector<IdentificationColor>(
+ controller_user_arg_new.identification_colors.begin(),
+ controller_user_arg_new.identification_colors.end()),
+ std::vector<ExplainText>(controller_user_arg_new.explain_text.begin(),
+ controller_user_arg_new.explain_text.end()));
+ }
+ }();
+
+ is_single_mode = parameters.enable_single_mode;
+
+ LOG_DEBUG(Service_HID,
+ "Controller Parameters: min_players={}, max_players={}, "
+ "keep_controllers_connected={}, enable_single_mode={}, enable_border_color={}, "
+ "enable_explain_text={}, allow_pro_controller={}, allow_handheld={}, "
+ "allow_dual_joycons={}, allow_left_joycon={}, allow_right_joycon={}",
+ parameters.min_players, parameters.max_players,
+ parameters.keep_controllers_connected, parameters.enable_single_mode,
+ parameters.enable_border_color, parameters.enable_explain_text,
+ parameters.allow_pro_controller, parameters.allow_handheld,
+ parameters.allow_dual_joycons, parameters.allow_left_joycon,
+ parameters.allow_right_joycon);
+
+ frontend.ReconfigureControllers([this] { ConfigurationComplete(); }, parameters);
+ break;
+ }
+ case ControllerSupportMode::ShowControllerStrapGuide:
+ case ControllerSupportMode::ShowControllerFirmwareUpdate:
+ UNIMPLEMENTED_MSG("ControllerSupportMode={} is not implemented",
+ controller_private_arg.mode);
+ [[fallthrough]];
+ default: {
+ ConfigurationComplete();
+ break;
+ }
+ }
+}
+
+void Controller::ConfigurationComplete() {
+ ControllerSupportResultInfo result_info{};
+
+ const auto& players = Settings::values.players.GetValue();
+
+ // If enable_single_mode is enabled, player_count is 1 regardless of any other parameters.
+ // Otherwise, only count connected players from P1-P8.
+ result_info.player_count =
+ is_single_mode ? 1
+ : static_cast<s8>(std::count_if(
+ players.begin(), players.end() - 2,
+ [](Settings::PlayerInput player) { return player.connected; }));
+
+ result_info.selected_id = HID::Controller_NPad::IndexToNPad(
+ std::distance(players.begin(),
+ std::find_if(players.begin(), players.end(),
+ [](Settings::PlayerInput player) { return player.connected; })));
+
+ result_info.result = 0;
+
+ LOG_DEBUG(Service_HID, "Result Info: player_count={}, selected_id={}, result={}",
+ result_info.player_count, result_info.selected_id, result_info.result);
+
+ complete = true;
+ out_data = std::vector<u8>(sizeof(ControllerSupportResultInfo));
+ std::memcpy(out_data.data(), &result_info, out_data.size());
+ broker.PushNormalDataFromApplet(std::make_shared<IStorage>(std::move(out_data)));
+ broker.SignalStateChanged();
+}
+
+} // namespace Service::AM::Applets
diff --git a/src/core/hle/service/am/applets/controller.h b/src/core/hle/service/am/applets/controller.h
new file mode 100644
index 000000000..a7a1f2b65
--- /dev/null
+++ b/src/core/hle/service/am/applets/controller.h
@@ -0,0 +1,135 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "core/hle/result.h"
+#include "core/hle/service/am/applets/applets.h"
+
+namespace Core {
+class System;
+}
+
+namespace Service::AM::Applets {
+
+using IdentificationColor = std::array<u8, 4>;
+using ExplainText = std::array<char, 0x81>;
+
+enum class ControllerAppletVersion : u32_le {
+ Version3 = 0x3, // 1.0.0 - 2.3.0
+ Version4 = 0x4, // 3.0.0 - 5.1.0
+ Version5 = 0x5, // 6.0.0 - 7.0.1
+ Version7 = 0x7, // 8.0.0+
+};
+
+enum class ControllerSupportMode : u8 {
+ ShowControllerSupport,
+ ShowControllerStrapGuide,
+ ShowControllerFirmwareUpdate,
+
+ MaxControllerSupportMode,
+};
+
+enum class ControllerSupportCaller : u8 {
+ Application,
+ System,
+
+ MaxControllerSupportCaller,
+};
+
+struct ControllerSupportArgPrivate {
+ u32 arg_private_size{};
+ u32 arg_size{};
+ bool flag_0{};
+ bool flag_1{};
+ ControllerSupportMode mode{};
+ ControllerSupportCaller caller{};
+ u32 style_set{};
+ u32 joy_hold_type{};
+};
+static_assert(sizeof(ControllerSupportArgPrivate) == 0x14,
+ "ControllerSupportArgPrivate has incorrect size.");
+
+struct ControllerSupportArgHeader {
+ s8 player_count_min{};
+ s8 player_count_max{};
+ bool enable_take_over_connection{};
+ bool enable_left_justify{};
+ bool enable_permit_joy_dual{};
+ bool enable_single_mode{};
+ bool enable_identification_color{};
+};
+static_assert(sizeof(ControllerSupportArgHeader) == 0x7,
+ "ControllerSupportArgHeader has incorrect size.");
+
+// LibraryAppletVersion 0x3, 0x4, 0x5
+struct ControllerSupportArgOld {
+ ControllerSupportArgHeader header{};
+ std::array<IdentificationColor, 4> identification_colors{};
+ bool enable_explain_text{};
+ std::array<ExplainText, 4> explain_text{};
+};
+static_assert(sizeof(ControllerSupportArgOld) == 0x21C,
+ "ControllerSupportArgOld has incorrect size.");
+
+// LibraryAppletVersion 0x7
+struct ControllerSupportArgNew {
+ ControllerSupportArgHeader header{};
+ std::array<IdentificationColor, 8> identification_colors{};
+ bool enable_explain_text{};
+ std::array<ExplainText, 8> explain_text{};
+};
+static_assert(sizeof(ControllerSupportArgNew) == 0x430,
+ "ControllerSupportArgNew has incorrect size.");
+
+struct ControllerUpdateFirmwareArg {
+ bool enable_force_update{};
+ INSERT_PADDING_BYTES(3);
+};
+static_assert(sizeof(ControllerUpdateFirmwareArg) == 0x4,
+ "ControllerUpdateFirmwareArg has incorrect size.");
+
+struct ControllerSupportResultInfo {
+ s8 player_count{};
+ INSERT_PADDING_BYTES(3);
+ u32 selected_id{};
+ u32 result{};
+};
+static_assert(sizeof(ControllerSupportResultInfo) == 0xC,
+ "ControllerSupportResultInfo has incorrect size.");
+
+class Controller final : public Applet {
+public:
+ explicit Controller(Core::System& system_, const Core::Frontend::ControllerApplet& frontend_);
+ ~Controller() override;
+
+ void Initialize() override;
+
+ bool TransactionComplete() const override;
+ ResultCode GetStatus() const override;
+ void ExecuteInteractive() override;
+ void Execute() override;
+
+ void ConfigurationComplete();
+
+private:
+ const Core::Frontend::ControllerApplet& frontend;
+
+ ControllerAppletVersion controller_applet_version;
+ ControllerSupportArgPrivate controller_private_arg;
+ ControllerSupportArgOld controller_user_arg_old;
+ ControllerSupportArgNew controller_user_arg_new;
+ ControllerUpdateFirmwareArg controller_update_arg;
+ bool complete{false};
+ ResultCode status{RESULT_SUCCESS};
+ bool is_single_mode{false};
+ std::vector<u8> out_data;
+};
+
+} // namespace Service::AM::Applets
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 @@
#include <numeric>
#include <vector>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/content_archive.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/nca_metadata.h"
@@ -163,7 +164,8 @@ void AOC_U::GetAddOnContentBaseId(Kernel::HLERequestContext& ctx) {
rb.Push(RESULT_SUCCESS);
const auto title_id = system.CurrentProcess()->GetTitleID();
- FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto res = pm.GetControlMetadata();
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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/apm/apm.h"
#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) {
}
PerformanceMode Controller::GetCurrentPerformanceMode() const {
- return Settings::values.use_docked_mode ? PerformanceMode::Docked : PerformanceMode::Handheld;
+ return Settings::values.use_docked_mode.GetValue() ? PerformanceMode::Docked
+ : PerformanceMode::Handheld;
}
PerformanceConfiguration Controller::GetCurrentPerformanceConfiguration(PerformanceMode mode) {
diff --git a/src/core/hle/service/audio/audren_u.cpp b/src/core/hle/service/audio/audren_u.cpp
index d8359abaa..a2d3ded7b 100644
--- a/src/core/hle/service/audio/audren_u.cpp
+++ b/src/core/hle/service/audio/audren_u.cpp
@@ -26,7 +26,7 @@ namespace Service::Audio {
class IAudioRenderer final : public ServiceFramework<IAudioRenderer> {
public:
- explicit IAudioRenderer(Core::System& system, AudioCore::AudioRendererParameter audren_params,
+ explicit IAudioRenderer(Core::System& system, AudioCommon::AudioRendererParameter audren_params,
const std::size_t instance_number)
: ServiceFramework("IAudioRenderer") {
// clang-format off
@@ -94,14 +94,15 @@ private:
void RequestUpdateImpl(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Audio, "(STUBBED) called");
- auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer());
+ std::vector<u8> output_params(ctx.GetWriteBufferSize());
+ auto result = renderer->UpdateAudioRenderer(ctx.ReadBuffer(), output_params);
- if (result.Succeeded()) {
- ctx.WriteBuffer(result.Unwrap());
+ if (result.IsSuccess()) {
+ ctx.WriteBuffer(output_params);
}
IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result.Code());
+ rb.Push(result);
}
void Start(Kernel::HLERequestContext& ctx) {
@@ -346,7 +347,7 @@ void AudRenU::OpenAudioRenderer(Kernel::HLERequestContext& ctx) {
OpenAudioRendererImpl(ctx);
}
-static u64 CalculateNumPerformanceEntries(const AudioCore::AudioRendererParameter& params) {
+static u64 CalculateNumPerformanceEntries(const AudioCommon::AudioRendererParameter& params) {
// +1 represents the final mix.
return u64{params.effect_count} + params.submix_count + params.sink_count + params.voice_count +
1;
@@ -375,7 +376,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
constexpr u64 upsampler_manager_size = 0x48;
// Calculates the part of the size that relates to mix buffers.
- const auto calculate_mix_buffer_sizes = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_mix_buffer_sizes = [](const AudioCommon::AudioRendererParameter& params) {
// As of 8.0.0 this is the maximum on voice channels.
constexpr u64 max_voice_channels = 6;
@@ -397,7 +398,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the portion of the size related to the mix data (and the sorting thereof).
- const auto calculate_mix_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_mix_info_size = [](const AudioCommon::AudioRendererParameter& params) {
// The size of the mixing info data structure.
constexpr u64 mix_info_size = 0x940;
@@ -447,7 +448,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to voice channel info.
- const auto calculate_voice_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_voice_info_size = [](const AudioCommon::AudioRendererParameter& params) {
constexpr u64 voice_info_size = 0x220;
constexpr u64 voice_resource_size = 0xD0;
@@ -461,7 +462,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to memory pools.
- const auto calculate_memory_pools_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_memory_pools_size = [](const AudioCommon::AudioRendererParameter& params) {
const u64 num_memory_pools = sizeof(s32) * (u64{params.effect_count} + params.voice_count);
const u64 memory_pool_info_size = 0x20;
return Common::AlignUp(num_memory_pools * memory_pool_info_size, info_field_alignment_size);
@@ -469,7 +470,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
// Calculates the part of the size related to the splitter context.
const auto calculate_splitter_context_size =
- [](const AudioCore::AudioRendererParameter& params) -> u64 {
+ [](const AudioCommon::AudioRendererParameter& params) -> u64 {
if (!IsFeatureSupported(AudioFeatures::Splitter, params.revision)) {
return 0;
}
@@ -488,27 +489,29 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to the upsampler info.
- const auto calculate_upsampler_info_size = [](const AudioCore::AudioRendererParameter& params) {
- constexpr u64 upsampler_info_size = 0x280;
- // Yes, using the buffer size over info alignment size is intentional here.
- return Common::AlignUp(upsampler_info_size * (u64{params.submix_count} + params.sink_count),
- buffer_alignment_size);
- };
+ const auto calculate_upsampler_info_size =
+ [](const AudioCommon::AudioRendererParameter& params) {
+ constexpr u64 upsampler_info_size = 0x280;
+ // Yes, using the buffer size over info alignment size is intentional here.
+ return Common::AlignUp(upsampler_info_size *
+ (u64{params.submix_count} + params.sink_count),
+ buffer_alignment_size);
+ };
// Calculates the part of the size related to effect info.
- const auto calculate_effect_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_effect_info_size = [](const AudioCommon::AudioRendererParameter& params) {
constexpr u64 effect_info_size = 0x2B0;
return Common::AlignUp(effect_info_size * params.effect_count, info_field_alignment_size);
};
// Calculates the part of the size related to audio sink info.
- const auto calculate_sink_info_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_sink_info_size = [](const AudioCommon::AudioRendererParameter& params) {
const u64 sink_info_size = 0x170;
return Common::AlignUp(sink_info_size * params.sink_count, info_field_alignment_size);
};
// Calculates the part of the size related to voice state info.
- const auto calculate_voice_state_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_voice_state_size = [](const AudioCommon::AudioRendererParameter& params) {
const u64 voice_state_size = 0x100;
const u64 additional_size = buffer_alignment_size - 1;
return Common::AlignUp(voice_state_size * params.voice_count + additional_size,
@@ -516,7 +519,7 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size related to performance statistics.
- const auto calculate_perf_size = [](const AudioCore::AudioRendererParameter& params) {
+ const auto calculate_perf_size = [](const AudioCommon::AudioRendererParameter& params) {
// Extra size value appended to the end of the calculation.
constexpr u64 appended = 128;
@@ -543,79 +546,81 @@ void AudRenU::GetAudioRendererWorkBufferSize(Kernel::HLERequestContext& ctx) {
};
// Calculates the part of the size that relates to the audio command buffer.
- const auto calculate_command_buffer_size = [](const AudioCore::AudioRendererParameter& params) {
- constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
+ const auto calculate_command_buffer_size =
+ [](const AudioCommon::AudioRendererParameter& params) {
+ constexpr u64 alignment = (buffer_alignment_size - 1) * 2;
- if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
- constexpr u64 command_buffer_size = 0x18000;
+ if (!IsFeatureSupported(AudioFeatures::VariadicCommandBuffer, params.revision)) {
+ constexpr u64 command_buffer_size = 0x18000;
- return command_buffer_size + alignment;
- }
+ return command_buffer_size + alignment;
+ }
- // When the variadic command buffer is supported, this means
- // the command generator for the audio renderer can issue commands
- // that are (as one would expect), variable in size. So what we need to do
- // is determine the maximum possible size for a few command data structures
- // then multiply them by the amount of present commands indicated by the given
- // respective audio parameters.
+ // When the variadic command buffer is supported, this means
+ // the command generator for the audio renderer can issue commands
+ // that are (as one would expect), variable in size. So what we need to do
+ // is determine the maximum possible size for a few command data structures
+ // then multiply them by the amount of present commands indicated by the given
+ // respective audio parameters.
- constexpr u64 max_biquad_filters = 2;
- constexpr u64 max_mix_buffers = 24;
+ constexpr u64 max_biquad_filters = 2;
+ constexpr u64 max_mix_buffers = 24;
- constexpr u64 biquad_filter_command_size = 0x2C;
+ constexpr u64 biquad_filter_command_size = 0x2C;
- constexpr u64 depop_mix_command_size = 0x24;
- constexpr u64 depop_setup_command_size = 0x50;
+ constexpr u64 depop_mix_command_size = 0x24;
+ constexpr u64 depop_setup_command_size = 0x50;
- constexpr u64 effect_command_max_size = 0x540;
+ constexpr u64 effect_command_max_size = 0x540;
- constexpr u64 mix_command_size = 0x1C;
- constexpr u64 mix_ramp_command_size = 0x24;
- constexpr u64 mix_ramp_grouped_command_size = 0x13C;
+ constexpr u64 mix_command_size = 0x1C;
+ constexpr u64 mix_ramp_command_size = 0x24;
+ constexpr u64 mix_ramp_grouped_command_size = 0x13C;
- constexpr u64 perf_command_size = 0x28;
+ constexpr u64 perf_command_size = 0x28;
- constexpr u64 sink_command_size = 0x130;
+ constexpr u64 sink_command_size = 0x130;
- constexpr u64 submix_command_max_size =
- depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
+ constexpr u64 submix_command_max_size =
+ depop_mix_command_size + (mix_command_size * max_mix_buffers) * max_mix_buffers;
- constexpr u64 volume_command_size = 0x1C;
- constexpr u64 volume_ramp_command_size = 0x20;
+ constexpr u64 volume_command_size = 0x1C;
+ constexpr u64 volume_ramp_command_size = 0x20;
- constexpr u64 voice_biquad_filter_command_size =
- biquad_filter_command_size * max_biquad_filters;
- constexpr u64 voice_data_command_size = 0x9C;
- const u64 voice_command_max_size =
- (params.splitter_count * depop_setup_command_size) +
- (voice_data_command_size + voice_biquad_filter_command_size + volume_ramp_command_size +
- mix_ramp_grouped_command_size);
+ constexpr u64 voice_biquad_filter_command_size =
+ biquad_filter_command_size * max_biquad_filters;
+ constexpr u64 voice_data_command_size = 0x9C;
+ const u64 voice_command_max_size =
+ (params.splitter_count * depop_setup_command_size) +
+ (voice_data_command_size + voice_biquad_filter_command_size +
+ volume_ramp_command_size + mix_ramp_grouped_command_size);
- // Now calculate the individual elements that comprise the size and add them together.
- const u64 effect_commands_size = params.effect_count * effect_command_max_size;
+ // Now calculate the individual elements that comprise the size and add them together.
+ const u64 effect_commands_size = params.effect_count * effect_command_max_size;
- const u64 final_mix_commands_size =
- depop_mix_command_size + volume_command_size * max_mix_buffers;
+ const u64 final_mix_commands_size =
+ depop_mix_command_size + volume_command_size * max_mix_buffers;
- const u64 perf_commands_size =
- perf_command_size * (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
+ const u64 perf_commands_size =
+ perf_command_size *
+ (CalculateNumPerformanceEntries(params) + max_perf_detail_entries);
- const u64 sink_commands_size = params.sink_count * sink_command_size;
+ const u64 sink_commands_size = params.sink_count * sink_command_size;
- const u64 splitter_commands_size =
- params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
+ const u64 splitter_commands_size =
+ params.num_splitter_send_channels * max_mix_buffers * mix_ramp_command_size;
- const u64 submix_commands_size = params.submix_count * submix_command_max_size;
+ const u64 submix_commands_size = params.submix_count * submix_command_max_size;
- const u64 voice_commands_size = params.voice_count * voice_command_max_size;
+ const u64 voice_commands_size = params.voice_count * voice_command_max_size;
- return effect_commands_size + final_mix_commands_size + perf_commands_size +
- sink_commands_size + splitter_commands_size + submix_commands_size +
- voice_commands_size + alignment;
- };
+ return effect_commands_size + final_mix_commands_size + perf_commands_size +
+ sink_commands_size + splitter_commands_size + submix_commands_size +
+ voice_commands_size + alignment;
+ };
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
+ const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
u64 size = 0;
size += calculate_mix_buffer_sizes(params);
@@ -681,7 +686,7 @@ void AudRenU::GetAudioDeviceServiceWithRevisionInfo(Kernel::HLERequestContext& c
void AudRenU::OpenAudioRendererImpl(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto params = rp.PopRaw<AudioCore::AudioRendererParameter>();
+ const auto params = rp.PopRaw<AudioCommon::AudioRendererParameter>();
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/bcat/backend/backend.cpp b/src/core/hle/service/bcat/backend/backend.cpp
index def3410cc..174388445 100644
--- a/src/core/hle/service/bcat/backend/backend.cpp
+++ b/src/core/hle/service/bcat/backend/backend.cpp
@@ -84,7 +84,7 @@ void ProgressServiceBackend::FinishDownload(ResultCode result) {
void ProgressServiceBackend::SignalUpdate() const {
if (need_hle_lock) {
- std::lock_guard<std::recursive_mutex> lock(HLE::g_hle_lock);
+ std::lock_guard lock(HLE::g_hle_lock);
event.writable->Signal();
} else {
event.writable->Signal();
diff --git a/src/core/hle/service/bcat/backend/boxcat.cpp b/src/core/hle/service/bcat/backend/boxcat.cpp
index ca021a99f..3b6f7498e 100644
--- a/src/core/hle/service/bcat/backend/boxcat.cpp
+++ b/src/core/hle/service/bcat/backend/boxcat.cpp
@@ -196,7 +196,9 @@ private:
const std::string& content_type_name) {
if (client == nullptr) {
client = std::make_unique<httplib::SSLClient>(BOXCAT_HOSTNAME, PORT);
- client->set_timeout_sec(timeout_seconds);
+ client->set_connection_timeout(timeout_seconds);
+ client->set_read_timeout(timeout_seconds);
+ client->set_write_timeout(timeout_seconds);
}
httplib::Headers headers{
@@ -255,7 +257,7 @@ private:
return out;
}
- std::unique_ptr<httplib::Client> client;
+ std::unique_ptr<httplib::SSLClient> client;
std::string path;
u64 title_id;
u64 build_id;
@@ -443,13 +445,25 @@ std::optional<std::vector<u8>> Boxcat::GetLaunchParameter(TitleIDVersion title)
Boxcat::StatusResult Boxcat::GetStatus(std::optional<std::string>& global,
std::map<std::string, EventStatus>& games) {
httplib::SSLClient client{BOXCAT_HOSTNAME, static_cast<int>(PORT)};
- client.set_timeout_sec(static_cast<int>(TIMEOUT_SECONDS));
+ client.set_connection_timeout(static_cast<int>(TIMEOUT_SECONDS));
+ client.set_read_timeout(static_cast<int>(TIMEOUT_SECONDS));
+ client.set_write_timeout(static_cast<int>(TIMEOUT_SECONDS));
httplib::Headers headers{
{std::string("Game-Assets-API-Version"), std::string(BOXCAT_API_VERSION)},
{std::string("Boxcat-Client-Type"), std::string(BOXCAT_CLIENT_TYPE)},
};
+ if (!client.is_valid()) {
+ LOG_ERROR(Service_BCAT, "Client is invalid, going offline!");
+ return StatusResult::Offline;
+ }
+
+ if (!client.is_socket_open()) {
+ LOG_ERROR(Service_BCAT, "Failed to open socket, going offline!");
+ return StatusResult::Offline;
+ }
+
const auto response = client.Get(BOXCAT_PATHNAME_EVENTS, headers);
if (response == nullptr)
return StatusResult::Offline;
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 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
+#include "core/core.h"
#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#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 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#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 @@
#include <memory>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/kernel/kernel.h"
diff --git a/src/core/hle/service/caps/caps_c.cpp b/src/core/hle/service/caps/caps_c.cpp
index ab17a187e..a0ee116fa 100644
--- a/src/core/hle/service/caps/caps_c.cpp
+++ b/src/core/hle/service/caps/caps_c.cpp
@@ -2,6 +2,8 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/logging/log.h"
+#include "core/hle/ipc_helpers.h"
#include "core/hle/service/caps/caps_c.h"
namespace Service::Capture {
@@ -47,7 +49,7 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") {
static const FunctionInfo functions[] = {
{1, nullptr, "CaptureRawImage"},
{2, nullptr, "CaptureRawImageWithTimeout"},
- {33, nullptr, "Unknown33"},
+ {33, &CAPS_C::SetShimLibraryVersion, "SetShimLibraryVersion"},
{1001, nullptr, "RequestTakingScreenShot"},
{1002, nullptr, "RequestTakingScreenShotWithTimeout"},
{1011, nullptr, "NotifyTakingScreenShotRefused"},
@@ -72,4 +74,16 @@ CAPS_C::CAPS_C() : ServiceFramework("caps:c") {
CAPS_C::~CAPS_C() = default;
+void CAPS_C::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto library_version{rp.Pop<u64>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
+ library_version, applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_c.h b/src/core/hle/service/caps/caps_c.h
index a9d028689..b110301d4 100644
--- a/src/core/hle/service/caps/caps_c.h
+++ b/src/core/hle/service/caps/caps_c.h
@@ -16,6 +16,9 @@ class CAPS_C final : public ServiceFramework<CAPS_C> {
public:
explicit CAPS_C();
~CAPS_C() override;
+
+private:
+ void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_su.cpp b/src/core/hle/service/caps/caps_su.cpp
index fffb2ecf9..e386470f7 100644
--- a/src/core/hle/service/caps/caps_su.cpp
+++ b/src/core/hle/service/caps/caps_su.cpp
@@ -25,7 +25,12 @@ CAPS_SU::CAPS_SU() : ServiceFramework("caps:su") {
CAPS_SU::~CAPS_SU() = default;
void CAPS_SU::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_Capture, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const auto library_version{rp.Pop<u64>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
+ library_version, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/caps/caps_u.cpp b/src/core/hle/service/caps/caps_u.cpp
index f36d8de2d..f9479bdb3 100644
--- a/src/core/hle/service/caps/caps_u.cpp
+++ b/src/core/hle/service/caps/caps_u.cpp
@@ -31,8 +31,7 @@ public:
CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
// clang-format off
static const FunctionInfo functions[] = {
- {31, nullptr, "GetShimLibraryVersion"},
- {32, nullptr, "SetShimLibraryVersion"},
+ {32, &CAPS_U::SetShimLibraryVersion, "SetShimLibraryVersion"},
{102, &CAPS_U::GetAlbumContentsFileListForApplication, "GetAlbumContentsFileListForApplication"},
{103, nullptr, "DeleteAlbumContentsFileForApplication"},
{104, nullptr, "GetAlbumContentsFileSizeForApplication"},
@@ -42,7 +41,7 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
{130, nullptr, "PrecheckToCreateContentsForApplication"},
{140, nullptr, "GetAlbumFileList1AafeAruidDeprecated"},
{141, nullptr, "GetAlbumFileList2AafeUidAruidDeprecated"},
- {142, nullptr, "GetAlbumFileList3AaeAruid"},
+ {142, &CAPS_U::GetAlbumFileList3AaeAruid, "GetAlbumFileList3AaeAruid"},
{143, nullptr, "GetAlbumFileList4AaeUidAruid"},
{60002, nullptr, "OpenAccessorSessionForApplication"},
};
@@ -53,6 +52,18 @@ CAPS_U::CAPS_U() : ServiceFramework("caps:u") {
CAPS_U::~CAPS_U() = default;
+void CAPS_U::SetShimLibraryVersion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto library_version{rp.Pop<u64>()};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ LOG_WARNING(Service_Capture, "(STUBBED) called. library_version={}, applet_resource_user_id={}",
+ library_version, applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx) {
// Takes a type-0x6 output buffer containing an array of ApplicationAlbumFileEntry, a PID, an
// u8 ContentType, two s64s, and an u64 AppletResourceUserId. Returns an output u64 for total
@@ -66,17 +77,24 @@ void CAPS_U::GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& c
// TODO: Update this when we implement the album.
// Currently we do not have a method of accessing album entries, set this to 0 for now.
- constexpr s32 total_entries{0};
+ constexpr u32 total_entries_1{};
+ constexpr u32 total_entries_2{};
- LOG_WARNING(Service_Capture,
- "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
- "end_posix_time={}, applet_resource_user_id={}, total_entries={}",
- pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
- total_entries);
+ LOG_WARNING(
+ Service_Capture,
+ "(STUBBED) called. pid={}, content_type={}, start_posix_time={}, "
+ "end_posix_time={}, applet_resource_user_id={}, total_entries_1={}, total_entries_2={}",
+ pid, content_type, start_posix_time, end_posix_time, applet_resource_user_id,
+ total_entries_1, total_entries_2);
- IPC::ResponseBuilder rb{ctx, 3};
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push(total_entries);
+ rb.Push(total_entries_1);
+ rb.Push(total_entries_2);
+}
+
+void CAPS_U::GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx) {
+ GetAlbumContentsFileListForApplication(ctx);
}
} // namespace Service::Capture
diff --git a/src/core/hle/service/caps/caps_u.h b/src/core/hle/service/caps/caps_u.h
index 689364de4..4b80f3156 100644
--- a/src/core/hle/service/caps/caps_u.h
+++ b/src/core/hle/service/caps/caps_u.h
@@ -18,7 +18,9 @@ public:
~CAPS_U() override;
private:
+ void SetShimLibraryVersion(Kernel::HLERequestContext& ctx);
void GetAlbumContentsFileListForApplication(Kernel::HLERequestContext& ctx);
+ void GetAlbumFileList3AaeAruid(Kernel::HLERequestContext& ctx);
};
} // namespace Service::Capture
diff --git a/src/core/hle/service/filesystem/filesystem.cpp b/src/core/hle/service/filesystem/filesystem.cpp
index 2cee1193c..2e53cae5b 100644
--- a/src/core/hle/service/filesystem/filesystem.cpp
+++ b/src/core/hle/service/filesystem/filesystem.cpp
@@ -79,7 +79,7 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
}
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
- if (dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
+ if (dir == nullptr || dir->GetFile(Common::FS::GetFilename(path)) == nullptr) {
return FileSys::ERROR_PATH_NOT_FOUND;
}
if (!dir->DeleteFile(Common::FS::GetFilename(path))) {
@@ -93,8 +93,9 @@ ResultCode VfsDirectoryServiceWrapper::DeleteFile(const std::string& path_) cons
ResultCode VfsDirectoryServiceWrapper::CreateDirectory(const std::string& path_) const {
std::string path(Common::FS::SanitizePath(path_));
auto dir = GetDirectoryRelativeWrapped(backing, Common::FS::GetParentPath(path));
- if (dir == nullptr && Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty())
+ if (dir == nullptr || Common::FS::GetFilename(Common::FS::GetParentPath(path)).empty()) {
dir = backing;
+ }
auto new_dir = dir->CreateSubdirectory(Common::FS::GetFilename(path));
if (new_dir == nullptr) {
// TODO(DarkLordZach): Find a better error code for this
@@ -379,7 +380,7 @@ ResultVal<FileSys::VirtualFile> FileSystemController::OpenBISPartitionStorage(
return FileSys::ERROR_ENTITY_NOT_FOUND;
}
- auto part = bis_factory->OpenPartitionStorage(id);
+ auto part = bis_factory->OpenPartitionStorage(id, system.GetFilesystem());
if (part == nullptr) {
return FileSys::ERROR_INVALID_ARGUMENT;
}
@@ -454,7 +455,9 @@ FileSys::SaveDataSize FileSystemController::ReadSaveDataSize(FileSys::SaveDataTy
const auto res = system.GetAppLoader().ReadControlData(nacp);
if (res != Loader::ResultStatus::Success) {
- FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID()};
+ const FileSys::PatchManager pm{system.CurrentProcess()->GetTitleID(),
+ system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto metadata = pm.GetControlMetadata();
const auto& nacp_unique = metadata.first;
@@ -727,7 +730,8 @@ void FileSystemController::CreateFactories(FileSys::VfsFilesystem& vfs, bool ove
void InstallInterfaces(Core::System& system) {
std::make_shared<FSP_LDR>()->InstallAsService(system.ServiceManager());
std::make_shared<FSP_PR>()->InstallAsService(system.ServiceManager());
- std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetReporter())
+ std::make_shared<FSP_SRV>(system.GetFileSystemController(), system.GetContentProvider(),
+ system.GetReporter())
->InstallAsService(system.ServiceManager());
}
diff --git a/src/core/hle/service/filesystem/fsp_srv.cpp b/src/core/hle/service/filesystem/fsp_srv.cpp
index 26fd87f58..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:
u64 next_entry_index = 0;
};
-FSP_SRV::FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter)
- : ServiceFramework("fsp-srv"), fsc(fsc), reporter(reporter) {
+FSP_SRV::FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
+ const Core::Reporter& reporter_)
+ : ServiceFramework("fsp-srv"), fsc(fsc_), content_provider{content_provider_},
+ reporter(reporter_) {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "OpenFileSystem"},
@@ -844,8 +846,7 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
return;
}
- FileSys::StorageId id;
-
+ FileSys::StorageId id{};
switch (parameters.space_id) {
case FileSys::SaveDataSpaceId::NandUser:
id = FileSys::StorageId::NandUser;
@@ -857,6 +858,10 @@ void FSP_SRV::OpenSaveDataFileSystem(Kernel::HLERequestContext& ctx) {
case FileSys::SaveDataSpaceId::NandSystem:
id = FileSys::StorageId::NandSystem;
break;
+ case FileSys::SaveDataSpaceId::TemporaryStorage:
+ case FileSys::SaveDataSpaceId::ProperSystem:
+ case FileSys::SaveDataSpaceId::SafeMode:
+ UNREACHABLE();
}
auto filesystem =
@@ -902,7 +907,14 @@ void FSP_SRV::ReadSaveDataFileSystemExtraDataWithMaskBySaveDataAttribute(
// Stub this to None for now, backend needs an impl to read/write the SaveDataExtraData
constexpr auto flags = static_cast<u32>(FileSys::SaveDataFlags::None);
- LOG_WARNING(Service_FS, "(STUBBED) called, flags={}", flags);
+ LOG_WARNING(Service_FS,
+ "(STUBBED) called, flags={}, space_id={}, attribute.title_id={:016X}\n"
+ "attribute.user_id={:016X}{:016X}, attribute.save_id={:016X}\n"
+ "attribute.type={}, attribute.rank={}, attribute.index={}",
+ flags, static_cast<u32>(parameters.space_id), parameters.attribute.title_id,
+ parameters.attribute.user_id[1], parameters.attribute.user_id[0],
+ parameters.attribute.save_id, static_cast<u32>(parameters.attribute.type),
+ static_cast<u32>(parameters.attribute.rank), parameters.attribute.index);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
@@ -958,7 +970,7 @@ void FSP_SRV::OpenDataStorageByDataId(Kernel::HLERequestContext& ctx) {
return;
}
- FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, fsc, content_provider};
auto storage = std::make_shared<IStorage>(
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;
}
namespace FileSys {
+class ContentProvider;
class FileSystemBackend;
-}
+} // namespace FileSys
namespace Service::FileSystem {
@@ -32,7 +33,8 @@ enum class LogMode : u32 {
class FSP_SRV final : public ServiceFramework<FSP_SRV> {
public:
- explicit FSP_SRV(FileSystemController& fsc, const Core::Reporter& reporter);
+ explicit FSP_SRV(FileSystemController& fsc_, const FileSys::ContentProvider& content_provider_,
+ const Core::Reporter& reporter_);
~FSP_SRV() override;
private:
@@ -55,6 +57,7 @@ private:
void OpenMultiCommitManager(Kernel::HLERequestContext& ctx);
FileSystemController& fsc;
+ const FileSys::ContentProvider& content_provider;
FileSys::VirtualFile romfs;
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 @@
#include <queue>
#include "common/logging/log.h"
#include "common/uuid.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/readable_event.h"
#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 @@
#include <memory>
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
diff --git a/src/core/hle/service/hid/controllers/controller_base.h b/src/core/hle/service/hid/controllers/controller_base.h
index 8bc69c372..f47a9e61c 100644
--- a/src/core/hle/service/hid/controllers/controller_base.h
+++ b/src/core/hle/service/hid/controllers/controller_base.h
@@ -31,6 +31,10 @@ public:
virtual void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t size) = 0;
+ // When the controller is requesting a motion update for the shared memory
+ virtual void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+ std::size_t size) {}
+
// Called when input devices should be loaded
virtual void OnLoadInputDevices() = 0;
diff --git a/src/core/hle/service/hid/controllers/keyboard.cpp b/src/core/hle/service/hid/controllers/keyboard.cpp
index 0b896d5ad..59b694cd4 100644
--- a/src/core/hle/service/hid/controllers/keyboard.cpp
+++ b/src/core/hle/service/hid/controllers/keyboard.cpp
@@ -42,8 +42,8 @@ void Controller_Keyboard::OnUpdate(const Core::Timing::CoreTiming& core_timing,
cur_entry.modifier = 0;
if (Settings::values.keyboard_enabled) {
for (std::size_t i = 0; i < keyboard_keys.size(); ++i) {
- cur_entry.key[i / KEYS_PER_BYTE] |=
- (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE));
+ auto& entry = cur_entry.key[i / KEYS_PER_BYTE];
+ entry = static_cast<u8>(entry | (keyboard_keys[i]->GetStatus() << (i % KEYS_PER_BYTE)));
}
for (std::size_t i = 0; i < keyboard_mods.size(); ++i) {
diff --git a/src/core/hle/service/hid/controllers/npad.cpp b/src/core/hle/service/hid/controllers/npad.cpp
index 0e7794dc7..e2539ded8 100644
--- a/src/core/hle/service/hid/controllers/npad.cpp
+++ b/src/core/hle/service/hid/controllers/npad.cpp
@@ -24,6 +24,7 @@ constexpr s32 HID_JOYSTICK_MAX = 0x7fff;
constexpr std::size_t NPAD_OFFSET = 0x9A00;
constexpr u32 BATTERY_FULL = 2;
constexpr u32 MAX_NPAD_ID = 7;
+constexpr std::size_t HANDHELD_INDEX = 8;
constexpr std::array<u32, 10> npad_id_list{
0, 1, 2, 3, 4, 5, 6, 7, NPAD_HANDHELD, NPAD_UNKNOWN,
};
@@ -33,19 +34,41 @@ enum class JoystickId : std::size_t {
Joystick_Right,
};
-static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type) {
+Controller_NPad::NPadControllerType Controller_NPad::MapSettingsTypeToNPad(
+ Settings::ControllerType type) {
switch (type) {
case Settings::ControllerType::ProController:
- return Controller_NPad::NPadControllerType::ProController;
- case Settings::ControllerType::DualJoycon:
- return Controller_NPad::NPadControllerType::JoyDual;
+ return NPadControllerType::ProController;
+ case Settings::ControllerType::DualJoyconDetached:
+ return NPadControllerType::JoyDual;
case Settings::ControllerType::LeftJoycon:
- return Controller_NPad::NPadControllerType::JoyLeft;
+ return NPadControllerType::JoyLeft;
case Settings::ControllerType::RightJoycon:
- return Controller_NPad::NPadControllerType::JoyRight;
+ return NPadControllerType::JoyRight;
+ case Settings::ControllerType::Handheld:
+ return NPadControllerType::Handheld;
default:
UNREACHABLE();
- return Controller_NPad::NPadControllerType::JoyDual;
+ return NPadControllerType::ProController;
+ }
+}
+
+Settings::ControllerType Controller_NPad::MapNPadToSettingsType(
+ Controller_NPad::NPadControllerType type) {
+ switch (type) {
+ case NPadControllerType::ProController:
+ return Settings::ControllerType::ProController;
+ case NPadControllerType::JoyDual:
+ return Settings::ControllerType::DualJoyconDetached;
+ case NPadControllerType::JoyLeft:
+ return Settings::ControllerType::LeftJoycon;
+ case NPadControllerType::JoyRight:
+ return Settings::ControllerType::RightJoycon;
+ case NPadControllerType::Handheld:
+ return Settings::ControllerType::Handheld;
+ default:
+ UNREACHABLE();
+ return Settings::ControllerType::ProController;
}
}
@@ -60,9 +83,9 @@ std::size_t Controller_NPad::NPadIdToIndex(u32 npad_id) {
case 6:
case 7:
return npad_id;
- case 8:
+ case HANDHELD_INDEX:
case NPAD_HANDHELD:
- return 8;
+ return HANDHELD_INDEX;
case 9:
case NPAD_UNKNOWN:
return 9;
@@ -83,7 +106,7 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
case 6:
case 7:
return static_cast<u32>(index);
- case 8:
+ case HANDHELD_INDEX:
return NPAD_HANDHELD;
case 9:
return NPAD_UNKNOWN;
@@ -94,27 +117,40 @@ u32 Controller_NPad::IndexToNPad(std::size_t index) {
}
Controller_NPad::Controller_NPad(Core::System& system) : ControllerBase(system), system(system) {}
-Controller_NPad::~Controller_NPad() = default;
-void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
+Controller_NPad::~Controller_NPad() {
+ OnRelease();
+}
+
+void Controller_NPad::InitNewlyAddedController(std::size_t controller_idx) {
const auto controller_type = connected_controllers[controller_idx].type;
auto& controller = shared_memory_entries[controller_idx];
if (controller_type == NPadControllerType::None) {
+ styleset_changed_events[controller_idx].writable->Signal();
return;
}
controller.joy_styles.raw = 0; // Zero out
controller.device_type.raw = 0;
+ controller.properties.raw = 0;
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
break;
+ case NPadControllerType::ProController:
+ controller.joy_styles.pro_controller.Assign(1);
+ controller.device_type.pro_controller.Assign(1);
+ controller.properties.is_vertical.Assign(1);
+ controller.properties.use_plus.Assign(1);
+ controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NpadAssignments::Single;
+ break;
case NPadControllerType::Handheld:
controller.joy_styles.handheld.Assign(1);
controller.device_type.handheld.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
+ controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyDual:
controller.joy_styles.joycon_dual.Assign(1);
@@ -123,34 +159,26 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.properties.is_vertical.Assign(1);
controller.properties.use_plus.Assign(1);
controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NPadAssignments::Dual;
+ controller.pad_assignment = NpadAssignments::Dual;
break;
case NPadControllerType::JoyLeft:
controller.joy_styles.joycon_left.Assign(1);
controller.device_type.joycon_left.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
+ controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::JoyRight:
controller.joy_styles.joycon_right.Assign(1);
controller.device_type.joycon_right.Assign(1);
controller.properties.is_horizontal.Assign(1);
controller.properties.use_plus.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
+ controller.pad_assignment = NpadAssignments::Single;
break;
case NPadControllerType::Pokeball:
controller.joy_styles.pokeball.Assign(1);
controller.device_type.pokeball.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
- break;
- case NPadControllerType::ProController:
- controller.joy_styles.pro_controller.Assign(1);
- controller.device_type.pro_controller.Assign(1);
- controller.properties.is_vertical.Assign(1);
- controller.properties.use_plus.Assign(1);
- controller.properties.use_minus.Assign(1);
- controller.pad_assignment = NPadAssignments::Single;
+ controller.pad_assignment = NpadAssignments::Single;
break;
}
@@ -159,21 +187,25 @@ void Controller_NPad::InitNewlyAddedControler(std::size_t controller_idx) {
controller.single_color.button_color = 0;
controller.dual_color_error = ColorReadError::ReadOk;
- controller.left_color.body_color = Settings::values.players[controller_idx].body_color_left;
- controller.left_color.button_color = Settings::values.players[controller_idx].button_color_left;
- controller.right_color.body_color = Settings::values.players[controller_idx].body_color_right;
+ controller.left_color.body_color =
+ Settings::values.players.GetValue()[controller_idx].body_color_left;
+ controller.left_color.button_color =
+ Settings::values.players.GetValue()[controller_idx].button_color_left;
+ controller.right_color.body_color =
+ Settings::values.players.GetValue()[controller_idx].body_color_right;
controller.right_color.button_color =
- Settings::values.players[controller_idx].button_color_right;
+ Settings::values.players.GetValue()[controller_idx].button_color_right;
controller.battery_level[0] = BATTERY_FULL;
controller.battery_level[1] = BATTERY_FULL;
controller.battery_level[2] = BATTERY_FULL;
- styleset_changed_events[controller_idx].writable->Signal();
+
+ SignalStyleSetChangedEvent(IndexToNPad(controller_idx));
}
void Controller_NPad::OnInit() {
auto& kernel = system.Kernel();
- for (std::size_t i = 0; i < styleset_changed_events.size(); i++) {
+ for (std::size_t i = 0; i < styleset_changed_events.size(); ++i) {
styleset_changed_events[i] = Kernel::WritableEvent::CreateEventPair(
kernel, fmt::format("npad:NpadStyleSetChanged_{}", i));
}
@@ -182,6 +214,8 @@ void Controller_NPad::OnInit() {
return;
}
+ OnLoadInputDevices();
+
if (style.raw == 0) {
// We want to support all controllers
style.handheld.Assign(1);
@@ -192,42 +226,46 @@ void Controller_NPad::OnInit() {
style.pokeball.Assign(1);
}
- std::transform(
- Settings::values.players.begin(), Settings::values.players.end(),
- connected_controllers.begin(), [](const Settings::PlayerInput& player) {
- return ControllerHolder{MapSettingsTypeToNPad(player.type), player.connected};
- });
+ std::transform(Settings::values.players.GetValue().begin(),
+ Settings::values.players.GetValue().end(), connected_controllers.begin(),
+ [](const Settings::PlayerInput& player) {
+ return ControllerHolder{MapSettingsTypeToNPad(player.controller_type),
+ player.connected};
+ });
- std::stable_partition(connected_controllers.begin(), connected_controllers.begin() + 8,
- [](const ControllerHolder& holder) { return holder.is_connected; });
+ // Connect the Player 1 or Handheld controller if none are connected.
+ if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
+ [](const ControllerHolder& controller) { return controller.is_connected; })) {
+ const auto controller =
+ MapSettingsTypeToNPad(Settings::values.players.GetValue()[0].controller_type);
+ if (controller == NPadControllerType::Handheld) {
+ Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
+ connected_controllers[HANDHELD_INDEX] = {controller, true};
+ } else {
+ Settings::values.players.GetValue()[0].connected = true;
+ connected_controllers[0] = {controller, true};
+ }
+ }
// Account for handheld
- if (connected_controllers[8].is_connected)
- connected_controllers[8].type = NPadControllerType::Handheld;
+ if (connected_controllers[HANDHELD_INDEX].is_connected) {
+ connected_controllers[HANDHELD_INDEX].type = NPadControllerType::Handheld;
+ }
supported_npad_id_types.resize(npad_id_list.size());
std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
npad_id_list.size() * sizeof(u32));
- // Add a default dual joycon controller if none are present.
- if (std::none_of(connected_controllers.begin(), connected_controllers.end(),
- [](const ControllerHolder& controller) { return controller.is_connected; })) {
- supported_npad_id_types.resize(npad_id_list.size());
- std::memcpy(supported_npad_id_types.data(), npad_id_list.data(),
- npad_id_list.size() * sizeof(u32));
- AddNewController(NPadControllerType::JoyDual);
- }
-
for (std::size_t i = 0; i < connected_controllers.size(); ++i) {
const auto& controller = connected_controllers[i];
if (controller.is_connected) {
- AddNewControllerAt(controller.type, IndexToNPad(i));
+ AddNewControllerAt(controller.type, i);
}
}
}
void Controller_NPad::OnLoadInputDevices() {
- const auto& players = Settings::values.players;
+ const auto& players = Settings::values.players.GetValue();
for (std::size_t i = 0; i < players.size(); ++i) {
std::transform(players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_BEGIN,
players[i].buttons.begin() + Settings::NativeButton::BUTTON_HID_END,
@@ -235,14 +273,30 @@ void Controller_NPad::OnLoadInputDevices() {
std::transform(players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_BEGIN,
players[i].analogs.begin() + Settings::NativeAnalog::STICK_HID_END,
sticks[i].begin(), Input::CreateDevice<Input::AnalogDevice>);
+ std::transform(players[i].vibrations.begin() +
+ Settings::NativeVibration::VIBRATION_HID_BEGIN,
+ players[i].vibrations.begin() + Settings::NativeVibration::VIBRATION_HID_END,
+ vibrations[i].begin(), Input::CreateDevice<Input::VibrationDevice>);
+ std::transform(players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_BEGIN,
+ players[i].motions.begin() + Settings::NativeMotion::MOTION_HID_END,
+ motions[i].begin(), Input::CreateDevice<Input::MotionDevice>);
+ for (std::size_t device_idx = 0; device_idx < vibrations[i].size(); ++device_idx) {
+ InitializeVibrationDeviceAtIndex(i, device_idx);
+ }
}
}
-void Controller_NPad::OnRelease() {}
+void Controller_NPad::OnRelease() {
+ for (std::size_t npad_idx = 0; npad_idx < vibrations.size(); ++npad_idx) {
+ for (std::size_t device_idx = 0; device_idx < vibrations[npad_idx].size(); ++device_idx) {
+ VibrateControllerAtIndex(npad_idx, device_idx, {});
+ }
+ }
+}
void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
const auto controller_idx = NPadIdToIndex(npad_id);
- [[maybe_unused]] const auto controller_type = connected_controllers[controller_idx].type;
+ const auto controller_type = connected_controllers[controller_idx].type;
if (!connected_controllers[controller_idx].is_connected) {
return;
}
@@ -257,61 +311,71 @@ void Controller_NPad::RequestPadStateUpdate(u32 npad_id) {
analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]->GetStatus();
using namespace Settings::NativeButton;
- pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
-
- pad_state.l_stick_right.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::RIGHT));
- pad_state.l_stick_left.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::LEFT));
- pad_state.l_stick_up.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::UP));
- pad_state.l_stick_down.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]->GetAnalogDirectionStatus(
- Input::AnalogDirection::DOWN));
-
- pad_state.r_stick_right.Assign(
- analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
- pad_state.r_stick_left.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
- pad_state.r_stick_up.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
- pad_state.r_stick_down.Assign(analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
- ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
-
- pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
- pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
-
- lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
- lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
- rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
- rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+ if (controller_type != NPadControllerType::JoyLeft) {
+ pad_state.a.Assign(button_state[A - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.b.Assign(button_state[B - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.x.Assign(button_state[X - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.y.Assign(button_state[Y - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r_stick.Assign(button_state[RStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.r.Assign(button_state[R - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zr.Assign(button_state[ZR - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.plus.Assign(button_state[Plus - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.r_stick_right.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
+ pad_state.r_stick_left.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
+ pad_state.r_stick_up.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
+ pad_state.r_stick_down.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Right)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
+ rstick_entry.x = static_cast<s32>(stick_r_x_f * HID_JOYSTICK_MAX);
+ rstick_entry.y = static_cast<s32>(stick_r_y_f * HID_JOYSTICK_MAX);
+ }
+
+ if (controller_type != NPadControllerType::JoyRight) {
+ pad_state.d_left.Assign(button_state[DLeft - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_up.Assign(button_state[DUp - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_right.Assign(button_state[DRight - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.d_down.Assign(button_state[DDown - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l_stick.Assign(button_state[LStick - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.l.Assign(button_state[L - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.zl.Assign(button_state[ZL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.minus.Assign(button_state[Minus - BUTTON_HID_BEGIN]->GetStatus());
+
+ pad_state.l_stick_right.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::RIGHT));
+ pad_state.l_stick_left.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::LEFT));
+ pad_state.l_stick_up.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::UP));
+ pad_state.l_stick_down.Assign(
+ analog_state[static_cast<std::size_t>(JoystickId::Joystick_Left)]
+ ->GetAnalogDirectionStatus(Input::AnalogDirection::DOWN));
+ lstick_entry.x = static_cast<s32>(stick_l_x_f * HID_JOYSTICK_MAX);
+ lstick_entry.y = static_cast<s32>(stick_l_y_f * HID_JOYSTICK_MAX);
+ }
+
+ if (controller_type == NPadControllerType::JoyLeft ||
+ controller_type == NPadControllerType::JoyRight) {
+ pad_state.left_sl.Assign(button_state[SL - BUTTON_HID_BEGIN]->GetStatus());
+ pad_state.left_sr.Assign(button_state[SR - BUTTON_HID_BEGIN]->GetStatus());
+ }
}
void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
std::size_t data_len) {
- if (!IsControllerActivated())
+ if (!IsControllerActivated()) {
return;
- for (std::size_t i = 0; i < shared_memory_entries.size(); i++) {
+ }
+ for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
auto& npad = shared_memory_entries[i];
const std::array<NPadGeneric*, 7> controller_npads{&npad.main_controller_states,
&npad.handheld_states,
@@ -344,6 +408,7 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
continue;
}
const u32 npad_index = static_cast<u32>(i);
+
RequestPadStateUpdate(npad_index);
auto& pad_state = npad_pad_states[npad_index];
@@ -360,13 +425,25 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
auto& libnx_entry = npad.libnx.npad[npad.libnx.common.last_entry_index];
libnx_entry.connection_status.raw = 0;
+ libnx_entry.connection_status.IsConnected.Assign(1);
switch (controller_type) {
case NPadControllerType::None:
UNREACHABLE();
break;
+ case NPadControllerType::ProController:
+ main_controller.connection_status.raw = 0;
+ main_controller.connection_status.IsConnected.Assign(1);
+ main_controller.connection_status.IsWired.Assign(1);
+ main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
+ main_controller.pad.l_stick = pad_state.l_stick;
+ main_controller.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsWired.Assign(1);
+ break;
case NPadControllerType::Handheld:
handheld_entry.connection_status.raw = 0;
+ handheld_entry.connection_status.IsConnected.Assign(1);
handheld_entry.connection_status.IsWired.Assign(1);
handheld_entry.connection_status.IsLeftJoyConnected.Assign(1);
handheld_entry.connection_status.IsRightJoyConnected.Assign(1);
@@ -375,57 +452,52 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
handheld_entry.pad.pad_states.raw = pad_state.pad_states.raw;
handheld_entry.pad.l_stick = pad_state.l_stick;
handheld_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsWired.Assign(1);
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsLeftJoyWired.Assign(1);
+ libnx_entry.connection_status.IsRightJoyWired.Assign(1);
break;
case NPadControllerType::JoyDual:
dual_entry.connection_status.raw = 0;
-
+ dual_entry.connection_status.IsConnected.Assign(1);
dual_entry.connection_status.IsLeftJoyConnected.Assign(1);
dual_entry.connection_status.IsRightJoyConnected.Assign(1);
- dual_entry.connection_status.IsConnected.Assign(1);
-
- libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
- libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
- libnx_entry.connection_status.IsConnected.Assign(1);
-
dual_entry.pad.pad_states.raw = pad_state.pad_states.raw;
dual_entry.pad.l_stick = pad_state.l_stick;
dual_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
break;
case NPadControllerType::JoyLeft:
left_entry.connection_status.raw = 0;
-
left_entry.connection_status.IsConnected.Assign(1);
+ left_entry.connection_status.IsLeftJoyConnected.Assign(1);
left_entry.pad.pad_states.raw = pad_state.pad_states.raw;
left_entry.pad.l_stick = pad_state.l_stick;
left_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsLeftJoyConnected.Assign(1);
break;
case NPadControllerType::JoyRight:
right_entry.connection_status.raw = 0;
-
right_entry.connection_status.IsConnected.Assign(1);
+ right_entry.connection_status.IsRightJoyConnected.Assign(1);
right_entry.pad.pad_states.raw = pad_state.pad_states.raw;
right_entry.pad.l_stick = pad_state.l_stick;
right_entry.pad.r_stick = pad_state.r_stick;
+
+ libnx_entry.connection_status.IsRightJoyConnected.Assign(1);
break;
case NPadControllerType::Pokeball:
pokeball_entry.connection_status.raw = 0;
-
pokeball_entry.connection_status.IsConnected.Assign(1);
- pokeball_entry.connection_status.IsWired.Assign(1);
-
pokeball_entry.pad.pad_states.raw = pad_state.pad_states.raw;
pokeball_entry.pad.l_stick = pad_state.l_stick;
pokeball_entry.pad.r_stick = pad_state.r_stick;
break;
- case NPadControllerType::ProController:
- main_controller.connection_status.raw = 0;
-
- main_controller.connection_status.IsConnected.Assign(1);
- main_controller.connection_status.IsWired.Assign(1);
- main_controller.pad.pad_states.raw = pad_state.pad_states.raw;
- main_controller.pad.l_stick = pad_state.l_stick;
- main_controller.pad.r_stick = pad_state.r_stick;
- break;
}
// LibNX exclusively uses this section, so we always update it since LibNX doesn't activate
@@ -440,39 +512,144 @@ void Controller_NPad::OnUpdate(const Core::Timing::CoreTiming& core_timing, u8*
shared_memory_entries.size() * sizeof(NPadEntry));
}
-void Controller_NPad::SetSupportedStyleSet(NPadType style_set) {
+void Controller_NPad::OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+ std::size_t data_len) {
+ if (!IsControllerActivated()) {
+ return;
+ }
+ for (std::size_t i = 0; i < shared_memory_entries.size(); ++i) {
+ auto& npad = shared_memory_entries[i];
+
+ const auto& controller_type = connected_controllers[i].type;
+
+ if (controller_type == NPadControllerType::None || !connected_controllers[i].is_connected) {
+ continue;
+ }
+
+ const std::array<SixAxisGeneric*, 6> controller_sixaxes{
+ &npad.sixaxis_full, &npad.sixaxis_handheld, &npad.sixaxis_dual_left,
+ &npad.sixaxis_dual_right, &npad.sixaxis_left, &npad.sixaxis_right,
+ };
+
+ for (auto* sixaxis_sensor : controller_sixaxes) {
+ sixaxis_sensor->common.entry_count = 16;
+ sixaxis_sensor->common.total_entry_count = 17;
+
+ const auto& last_entry =
+ sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+ sixaxis_sensor->common.timestamp = core_timing.GetCPUTicks();
+ sixaxis_sensor->common.last_entry_index =
+ (sixaxis_sensor->common.last_entry_index + 1) % 17;
+
+ auto& cur_entry = sixaxis_sensor->sixaxis[sixaxis_sensor->common.last_entry_index];
+
+ cur_entry.timestamp = last_entry.timestamp + 1;
+ cur_entry.timestamp2 = cur_entry.timestamp;
+ }
+
+ // Try to read sixaxis sensor states
+ std::array<MotionDevice, 2> motion_devices;
+
+ if (sixaxis_sensors_enabled && Settings::values.motion_enabled.GetValue()) {
+ sixaxis_at_rest = true;
+ for (std::size_t e = 0; e < motion_devices.size(); ++e) {
+ const auto& device = motions[i][e];
+ if (device) {
+ std::tie(motion_devices[e].accel, motion_devices[e].gyro,
+ motion_devices[e].rotation, motion_devices[e].orientation) =
+ device->GetStatus();
+ sixaxis_at_rest = sixaxis_at_rest && motion_devices[e].gyro.Length2() < 0.0001f;
+ }
+ }
+ }
+
+ auto& full_sixaxis_entry =
+ npad.sixaxis_full.sixaxis[npad.sixaxis_full.common.last_entry_index];
+ auto& handheld_sixaxis_entry =
+ npad.sixaxis_handheld.sixaxis[npad.sixaxis_handheld.common.last_entry_index];
+ auto& dual_left_sixaxis_entry =
+ npad.sixaxis_dual_left.sixaxis[npad.sixaxis_dual_left.common.last_entry_index];
+ auto& dual_right_sixaxis_entry =
+ npad.sixaxis_dual_right.sixaxis[npad.sixaxis_dual_right.common.last_entry_index];
+ auto& left_sixaxis_entry =
+ npad.sixaxis_left.sixaxis[npad.sixaxis_left.common.last_entry_index];
+ auto& right_sixaxis_entry =
+ npad.sixaxis_right.sixaxis[npad.sixaxis_right.common.last_entry_index];
+
+ switch (controller_type) {
+ case NPadControllerType::None:
+ UNREACHABLE();
+ break;
+ case NPadControllerType::ProController:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ full_sixaxis_entry.accel = motion_devices[0].accel;
+ full_sixaxis_entry.gyro = motion_devices[0].gyro;
+ full_sixaxis_entry.rotation = motion_devices[0].rotation;
+ full_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ break;
+ case NPadControllerType::Handheld:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ handheld_sixaxis_entry.accel = motion_devices[0].accel;
+ handheld_sixaxis_entry.gyro = motion_devices[0].gyro;
+ handheld_sixaxis_entry.rotation = motion_devices[0].rotation;
+ handheld_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ break;
+ case NPadControllerType::JoyDual:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ // Set motion for the left joycon
+ dual_left_sixaxis_entry.accel = motion_devices[0].accel;
+ dual_left_sixaxis_entry.gyro = motion_devices[0].gyro;
+ dual_left_sixaxis_entry.rotation = motion_devices[0].rotation;
+ dual_left_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ if (sixaxis_sensors_enabled && motions[i][1]) {
+ // Set motion for the right joycon
+ dual_right_sixaxis_entry.accel = motion_devices[1].accel;
+ dual_right_sixaxis_entry.gyro = motion_devices[1].gyro;
+ dual_right_sixaxis_entry.rotation = motion_devices[1].rotation;
+ dual_right_sixaxis_entry.orientation = motion_devices[1].orientation;
+ }
+ break;
+ case NPadControllerType::JoyLeft:
+ if (sixaxis_sensors_enabled && motions[i][0]) {
+ left_sixaxis_entry.accel = motion_devices[0].accel;
+ left_sixaxis_entry.gyro = motion_devices[0].gyro;
+ left_sixaxis_entry.rotation = motion_devices[0].rotation;
+ left_sixaxis_entry.orientation = motion_devices[0].orientation;
+ }
+ break;
+ case NPadControllerType::JoyRight:
+ if (sixaxis_sensors_enabled && motions[i][1]) {
+ right_sixaxis_entry.accel = motion_devices[1].accel;
+ right_sixaxis_entry.gyro = motion_devices[1].gyro;
+ right_sixaxis_entry.rotation = motion_devices[1].rotation;
+ right_sixaxis_entry.orientation = motion_devices[1].orientation;
+ }
+ break;
+ case NPadControllerType::Pokeball:
+ break;
+ }
+ }
+ std::memcpy(data + NPAD_OFFSET, shared_memory_entries.data(),
+ shared_memory_entries.size() * sizeof(NPadEntry));
+}
+
+void Controller_NPad::SetSupportedStyleSet(NpadStyleSet style_set) {
style.raw = style_set.raw;
}
-Controller_NPad::NPadType Controller_NPad::GetSupportedStyleSet() const {
+Controller_NPad::NpadStyleSet Controller_NPad::GetSupportedStyleSet() const {
return style;
}
-void Controller_NPad::SetSupportedNPadIdTypes(u8* data, std::size_t length) {
+void Controller_NPad::SetSupportedNpadIdTypes(u8* data, std::size_t length) {
ASSERT(length > 0 && (length % sizeof(u32)) == 0);
supported_npad_id_types.clear();
supported_npad_id_types.resize(length / sizeof(u32));
std::memcpy(supported_npad_id_types.data(), data, length);
- for (std::size_t i = 0; i < connected_controllers.size(); i++) {
- auto& controller = connected_controllers[i];
- if (!controller.is_connected) {
- continue;
- }
- const auto requested_controller =
- i <= MAX_NPAD_ID ? MapSettingsTypeToNPad(Settings::values.players[i].type)
- : NPadControllerType::Handheld;
- if (!IsControllerSupported(requested_controller)) {
- const auto is_handheld = requested_controller == NPadControllerType::Handheld;
- if (is_handheld) {
- controller.type = NPadControllerType::None;
- controller.is_connected = false;
- AddNewController(requested_controller);
- } else {
- controller.type = requested_controller;
- InitNewlyAddedControler(i);
- }
- }
- }
}
void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length) {
@@ -480,7 +657,7 @@ void Controller_NPad::GetSupportedNpadIdTypes(u32* data, std::size_t max_length)
std::memcpy(data, supported_npad_id_types.data(), supported_npad_id_types.size());
}
-std::size_t Controller_NPad::GetSupportedNPadIdTypesSize() const {
+std::size_t Controller_NPad::GetSupportedNpadIdTypesSize() const {
return supported_npad_id_types.size();
}
@@ -492,7 +669,15 @@ Controller_NPad::NpadHoldType Controller_NPad::GetHoldType() const {
return hold_type;
}
-void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode) {
+void Controller_NPad::SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode) {
+ handheld_activation_mode = activation_mode;
+}
+
+Controller_NPad::NpadHandheldActivationMode Controller_NPad::GetNpadHandheldActivationMode() const {
+ return handheld_activation_mode;
+}
+
+void Controller_NPad::SetNpadMode(u32 npad_id, NpadAssignments assignment_mode) {
const std::size_t npad_index = NPadIdToIndex(npad_id);
ASSERT(npad_index < shared_memory_entries.size());
if (shared_memory_entries[npad_index].pad_assignment != assignment_mode) {
@@ -500,70 +685,198 @@ void Controller_NPad::SetNpadMode(u32 npad_id, NPadAssignments assignment_mode)
}
}
-void Controller_NPad::VibrateController(const std::vector<u32>& controller_ids,
- const std::vector<Vibration>& vibrations) {
- LOG_DEBUG(Service_HID, "(STUBBED) called");
+bool Controller_NPad::VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
+ const VibrationValue& vibration_value) {
+ if (!connected_controllers[npad_index].is_connected || !vibrations[npad_index][device_index]) {
+ return false;
+ }
- if (!can_controllers_vibrate) {
- return;
+ const auto& player = Settings::values.players.GetValue()[npad_index];
+
+ if (!player.vibration_enabled) {
+ if (latest_vibration_values[npad_index][device_index].amp_low != 0.0f ||
+ latest_vibration_values[npad_index][device_index].amp_high != 0.0f) {
+ // Send an empty vibration to stop any vibrations.
+ vibrations[npad_index][device_index]->SetRumblePlay(0.0f, 160.0f, 0.0f, 320.0f);
+ // Then reset the vibration value to its default value.
+ latest_vibration_values[npad_index][device_index] = {};
+ }
+
+ return false;
}
- for (std::size_t i = 0; i < controller_ids.size(); i++) {
- std::size_t controller_pos = NPadIdToIndex(static_cast<u32>(i));
- if (connected_controllers[controller_pos].is_connected) {
- // TODO(ogniK): Vibrate the physical controller
+
+ if (!Settings::values.enable_accurate_vibrations.GetValue()) {
+ using std::chrono::duration_cast;
+ using std::chrono::milliseconds;
+ using std::chrono::steady_clock;
+
+ const auto now = steady_clock::now();
+
+ // Filter out non-zero vibrations that are within 10ms of each other.
+ if ((vibration_value.amp_low != 0.0f || vibration_value.amp_high != 0.0f) &&
+ duration_cast<milliseconds>(now - last_vibration_timepoints[npad_index][device_index]) <
+ milliseconds(10)) {
+ return false;
}
+
+ last_vibration_timepoints[npad_index][device_index] = now;
+ }
+
+ auto& vibration = vibrations[npad_index][device_index];
+ const auto player_vibration_strength = static_cast<f32>(player.vibration_strength);
+ const auto amp_low =
+ std::min(vibration_value.amp_low * player_vibration_strength / 100.0f, 1.0f);
+ const auto amp_high =
+ std::min(vibration_value.amp_high * player_vibration_strength / 100.0f, 1.0f);
+ return vibration->SetRumblePlay(amp_low, vibration_value.freq_low, amp_high,
+ vibration_value.freq_high);
+}
+
+void Controller_NPad::VibrateController(const DeviceHandle& vibration_device_handle,
+ const VibrationValue& vibration_value) {
+ if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
+ return;
+ }
+
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+
+ if (!vibration_devices_mounted[npad_index][device_index] ||
+ !connected_controllers[npad_index].is_connected) {
+ return;
+ }
+
+ if (vibration_device_handle.device_index == DeviceIndex::None) {
+ UNREACHABLE_MSG("DeviceIndex should never be None!");
+ return;
+ }
+
+ // Some games try to send mismatched parameters in the device handle, block these.
+ if ((connected_controllers[npad_index].type == NPadControllerType::JoyLeft &&
+ (vibration_device_handle.npad_type == NpadType::JoyconRight ||
+ vibration_device_handle.device_index == DeviceIndex::Right)) ||
+ (connected_controllers[npad_index].type == NPadControllerType::JoyRight &&
+ (vibration_device_handle.npad_type == NpadType::JoyconLeft ||
+ vibration_device_handle.device_index == DeviceIndex::Left))) {
+ return;
+ }
+
+ // Filter out vibrations with equivalent values to reduce unnecessary state changes.
+ if (vibration_value.amp_low == latest_vibration_values[npad_index][device_index].amp_low &&
+ vibration_value.amp_high == latest_vibration_values[npad_index][device_index].amp_high) {
+ return;
+ }
+
+ if (VibrateControllerAtIndex(npad_index, device_index, vibration_value)) {
+ latest_vibration_values[npad_index][device_index] = vibration_value;
+ }
+}
+
+void Controller_NPad::VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
+ const std::vector<VibrationValue>& vibration_values) {
+ if (!Settings::values.vibration_enabled.GetValue() && !permit_vibration_session_enabled) {
+ return;
+ }
+
+ ASSERT_OR_EXECUTE_MSG(
+ vibration_device_handles.size() == vibration_values.size(), { return; },
+ "The amount of device handles does not match with the amount of vibration values,"
+ "this is undefined behavior!");
+
+ for (std::size_t i = 0; i < vibration_device_handles.size(); ++i) {
+ VibrateController(vibration_device_handles[i], vibration_values[i]);
+ }
+}
+
+Controller_NPad::VibrationValue Controller_NPad::GetLastVibration(
+ const DeviceHandle& vibration_device_handle) const {
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+ return latest_vibration_values[npad_index][device_index];
+}
+
+void Controller_NPad::InitializeVibrationDevice(const DeviceHandle& vibration_device_handle) {
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+ InitializeVibrationDeviceAtIndex(npad_index, device_index);
+}
+
+void Controller_NPad::InitializeVibrationDeviceAtIndex(std::size_t npad_index,
+ std::size_t device_index) {
+ if (vibrations[npad_index][device_index]) {
+ vibration_devices_mounted[npad_index][device_index] =
+ vibrations[npad_index][device_index]->GetStatus() == 1;
+ } else {
+ vibration_devices_mounted[npad_index][device_index] = false;
}
- last_processed_vibration = vibrations.back();
+}
+
+void Controller_NPad::SetPermitVibrationSession(bool permit_vibration_session) {
+ permit_vibration_session_enabled = permit_vibration_session;
+}
+
+bool Controller_NPad::IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const {
+ const auto npad_index = NPadIdToIndex(vibration_device_handle.npad_id);
+ const auto device_index = static_cast<std::size_t>(vibration_device_handle.device_index);
+ return vibration_devices_mounted[npad_index][device_index];
}
std::shared_ptr<Kernel::ReadableEvent> Controller_NPad::GetStyleSetChangedEvent(u32 npad_id) const {
- // TODO(ogniK): Figure out the best time to signal this event. This event seems that it should
- // be signalled at least once, and signaled after a new controller is connected?
const auto& styleset_event = styleset_changed_events[NPadIdToIndex(npad_id)];
return styleset_event.readable;
}
-Controller_NPad::Vibration Controller_NPad::GetLastVibration() const {
- return last_processed_vibration;
+void Controller_NPad::SignalStyleSetChangedEvent(u32 npad_id) const {
+ styleset_changed_events[NPadIdToIndex(npad_id)].writable->Signal();
}
-void Controller_NPad::AddNewController(NPadControllerType controller) {
- controller = DecideBestController(controller);
- if (controller == NPadControllerType::Handheld) {
- connected_controllers[8] = {controller, true};
- InitNewlyAddedControler(8);
- return;
- }
- const auto pos =
- std::find_if(connected_controllers.begin(), connected_controllers.end() - 2,
- [](const ControllerHolder& holder) { return !holder.is_connected; });
- if (pos == connected_controllers.end() - 2) {
- LOG_ERROR(Service_HID, "Cannot connect any more controllers!");
+void Controller_NPad::AddNewControllerAt(NPadControllerType controller, std::size_t npad_index) {
+ UpdateControllerAt(controller, npad_index, true);
+}
+
+void Controller_NPad::UpdateControllerAt(NPadControllerType controller, std::size_t npad_index,
+ bool connected) {
+ if (!connected) {
+ DisconnectNpadAtIndex(npad_index);
return;
}
- const auto controller_id = std::distance(connected_controllers.begin(), pos);
- connected_controllers[controller_id] = {controller, true};
- InitNewlyAddedControler(controller_id);
-}
-void Controller_NPad::AddNewControllerAt(NPadControllerType controller, u32 npad_id) {
- controller = DecideBestController(controller);
if (controller == NPadControllerType::Handheld) {
- connected_controllers[NPadIdToIndex(NPAD_HANDHELD)] = {controller, true};
- InitNewlyAddedControler(NPadIdToIndex(NPAD_HANDHELD));
+ Settings::values.players.GetValue()[HANDHELD_INDEX].controller_type =
+ MapNPadToSettingsType(controller);
+ Settings::values.players.GetValue()[HANDHELD_INDEX].connected = true;
+ connected_controllers[HANDHELD_INDEX] = {controller, true};
+ InitNewlyAddedController(HANDHELD_INDEX);
return;
}
- connected_controllers[NPadIdToIndex(npad_id)] = {controller, true};
- InitNewlyAddedControler(NPadIdToIndex(npad_id));
+ Settings::values.players.GetValue()[npad_index].controller_type =
+ MapNPadToSettingsType(controller);
+ Settings::values.players.GetValue()[npad_index].connected = true;
+ connected_controllers[npad_index] = {controller, true};
+ InitNewlyAddedController(npad_index);
}
-void Controller_NPad::ConnectNPad(u32 npad_id) {
- connected_controllers[NPadIdToIndex(npad_id)].is_connected = true;
+void Controller_NPad::DisconnectNpad(u32 npad_id) {
+ DisconnectNpadAtIndex(NPadIdToIndex(npad_id));
}
-void Controller_NPad::DisconnectNPad(u32 npad_id) {
- connected_controllers[NPadIdToIndex(npad_id)].is_connected = false;
+void Controller_NPad::DisconnectNpadAtIndex(std::size_t npad_index) {
+ for (std::size_t device_idx = 0; device_idx < vibrations[npad_index].size(); ++device_idx) {
+ // Send an empty vibration to stop any vibrations.
+ VibrateControllerAtIndex(npad_index, device_idx, {});
+ vibration_devices_mounted[npad_index][device_idx] = false;
+ }
+
+ Settings::values.players.GetValue()[npad_index].connected = false;
+ connected_controllers[npad_index].is_connected = false;
+
+ auto& controller = shared_memory_entries[npad_index];
+ controller.joy_styles.raw = 0; // Zero out
+ controller.device_type.raw = 0;
+ controller.properties.raw = 0;
+
+ SignalStyleSetChangedEvent(IndexToNPad(npad_index));
}
void Controller_NPad::SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode) {
@@ -574,6 +887,30 @@ Controller_NPad::GyroscopeZeroDriftMode Controller_NPad::GetGyroscopeZeroDriftMo
return gyroscope_zero_drift_mode;
}
+bool Controller_NPad::IsSixAxisSensorAtRest() const {
+ return sixaxis_at_rest;
+}
+
+void Controller_NPad::SetSixAxisEnabled(bool six_axis_status) {
+ sixaxis_sensors_enabled = six_axis_status;
+}
+
+void Controller_NPad::MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2) {
+ const auto npad_index_1 = NPadIdToIndex(npad_id_1);
+ const auto npad_index_2 = NPadIdToIndex(npad_id_2);
+
+ // If the controllers at both npad indices form a pair of left and right joycons, merge them.
+ // Otherwise, do nothing.
+ if ((connected_controllers[npad_index_1].type == NPadControllerType::JoyLeft &&
+ connected_controllers[npad_index_2].type == NPadControllerType::JoyRight) ||
+ (connected_controllers[npad_index_2].type == NPadControllerType::JoyLeft &&
+ connected_controllers[npad_index_1].type == NPadControllerType::JoyRight)) {
+ // Disconnect the joycon at the second id and connect the dual joycon at the first index.
+ DisconnectNpad(npad_id_2);
+ AddNewControllerAt(NPadControllerType::JoyDual, npad_index_1);
+ }
+}
+
void Controller_NPad::StartLRAssignmentMode() {
// Nothing internally is used for lr assignment mode. Since we have the ability to set the
// controller types from boot, it doesn't really matter about showing a selection screen
@@ -599,8 +936,8 @@ bool Controller_NPad::SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2) {
std::swap(connected_controllers[npad_index_1].type, connected_controllers[npad_index_2].type);
- InitNewlyAddedControler(npad_index_1);
- InitNewlyAddedControler(npad_index_2);
+ AddNewControllerAt(connected_controllers[npad_index_1].type, npad_index_1);
+ AddNewControllerAt(connected_controllers[npad_index_2].type, npad_index_2);
return true;
}
@@ -614,11 +951,11 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
case 0:
return LedPattern{1, 0, 0, 0};
case 1:
- return LedPattern{0, 1, 0, 0};
+ return LedPattern{1, 1, 0, 0};
case 2:
- return LedPattern{0, 0, 1, 0};
+ return LedPattern{1, 1, 1, 0};
case 3:
- return LedPattern{0, 0, 0, 1};
+ return LedPattern{1, 1, 1, 1};
case 4:
return LedPattern{1, 0, 0, 1};
case 5:
@@ -628,17 +965,17 @@ Controller_NPad::LedPattern Controller_NPad::GetLedPattern(u32 npad_id) {
case 7:
return LedPattern{0, 1, 1, 0};
default:
- UNIMPLEMENTED_MSG("Unhandled npad_id {}", npad_id);
return LedPattern{0, 0, 0, 0};
}
}
-void Controller_NPad::SetVibrationEnabled(bool can_vibrate) {
- can_controllers_vibrate = can_vibrate;
+bool Controller_NPad::IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const {
+ return unintended_home_button_input_protection[NPadIdToIndex(npad_id)];
}
-bool Controller_NPad::IsVibrationEnabled() const {
- return can_controllers_vibrate;
+void Controller_NPad::SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled,
+ u32 npad_id) {
+ unintended_home_button_input_protection[NPadIdToIndex(npad_id)] = is_protection_enabled;
}
void Controller_NPad::ClearAllConnectedControllers() {
@@ -651,13 +988,13 @@ void Controller_NPad::ClearAllConnectedControllers() {
}
void Controller_NPad::DisconnectAllConnectedControllers() {
- for (ControllerHolder& controller : connected_controllers) {
+ for (auto& controller : connected_controllers) {
controller.is_connected = false;
}
}
void Controller_NPad::ConnectAllDisconnectedControllers() {
- for (ControllerHolder& controller : connected_controllers) {
+ for (auto& controller : connected_controllers) {
if (controller.type != NPadControllerType::None && !controller.is_connected) {
controller.is_connected = true;
}
@@ -665,7 +1002,7 @@ void Controller_NPad::ConnectAllDisconnectedControllers() {
}
void Controller_NPad::ClearAllControllers() {
- for (ControllerHolder& controller : connected_controllers) {
+ for (auto& controller : connected_controllers) {
controller.type = NPadControllerType::None;
controller.is_connected = false;
}
@@ -685,7 +1022,7 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
// Handheld should not be supported in docked mode
- if (Settings::values.use_docked_mode) {
+ if (Settings::values.use_docked_mode.GetValue()) {
return false;
}
@@ -713,92 +1050,4 @@ bool Controller_NPad::IsControllerSupported(NPadControllerType controller) const
return false;
}
-Controller_NPad::NPadControllerType Controller_NPad::DecideBestController(
- NPadControllerType priority) const {
- if (IsControllerSupported(priority)) {
- return priority;
- }
- const auto is_docked = Settings::values.use_docked_mode;
- if (is_docked && priority == NPadControllerType::Handheld) {
- priority = NPadControllerType::JoyDual;
- if (IsControllerSupported(priority)) {
- return priority;
- }
- }
- std::vector<NPadControllerType> priority_list;
- switch (priority) {
- case NPadControllerType::ProController:
- priority_list.push_back(NPadControllerType::JoyDual);
- if (!is_docked) {
- priority_list.push_back(NPadControllerType::Handheld);
- }
- priority_list.push_back(NPadControllerType::JoyLeft);
- priority_list.push_back(NPadControllerType::JoyRight);
- priority_list.push_back(NPadControllerType::Pokeball);
- break;
- case NPadControllerType::Handheld:
- priority_list.push_back(NPadControllerType::JoyDual);
- priority_list.push_back(NPadControllerType::ProController);
- priority_list.push_back(NPadControllerType::JoyLeft);
- priority_list.push_back(NPadControllerType::JoyRight);
- priority_list.push_back(NPadControllerType::Pokeball);
- break;
- case NPadControllerType::JoyDual:
- if (!is_docked) {
- priority_list.push_back(NPadControllerType::Handheld);
- }
- priority_list.push_back(NPadControllerType::ProController);
- priority_list.push_back(NPadControllerType::JoyLeft);
- priority_list.push_back(NPadControllerType::JoyRight);
- priority_list.push_back(NPadControllerType::Pokeball);
- break;
- case NPadControllerType::JoyLeft:
- priority_list.push_back(NPadControllerType::JoyRight);
- priority_list.push_back(NPadControllerType::JoyDual);
- if (!is_docked) {
- priority_list.push_back(NPadControllerType::Handheld);
- }
- priority_list.push_back(NPadControllerType::ProController);
- priority_list.push_back(NPadControllerType::Pokeball);
- break;
- case NPadControllerType::JoyRight:
- priority_list.push_back(NPadControllerType::JoyLeft);
- priority_list.push_back(NPadControllerType::JoyDual);
- if (!is_docked) {
- priority_list.push_back(NPadControllerType::Handheld);
- }
- priority_list.push_back(NPadControllerType::ProController);
- priority_list.push_back(NPadControllerType::Pokeball);
- break;
- case NPadControllerType::Pokeball:
- priority_list.push_back(NPadControllerType::JoyLeft);
- priority_list.push_back(NPadControllerType::JoyRight);
- priority_list.push_back(NPadControllerType::JoyDual);
- if (!is_docked) {
- priority_list.push_back(NPadControllerType::Handheld);
- }
- priority_list.push_back(NPadControllerType::ProController);
- break;
- default:
- priority_list.push_back(NPadControllerType::JoyDual);
- if (!is_docked) {
- priority_list.push_back(NPadControllerType::Handheld);
- }
- priority_list.push_back(NPadControllerType::ProController);
- priority_list.push_back(NPadControllerType::JoyLeft);
- priority_list.push_back(NPadControllerType::JoyRight);
- priority_list.push_back(NPadControllerType::JoyDual);
- break;
- }
-
- const auto iter = std::find_if(priority_list.begin(), priority_list.end(),
- [this](auto type) { return IsControllerSupported(type); });
- if (iter == priority_list.end()) {
- UNIMPLEMENTED_MSG("Could not find supported controller!");
- return priority;
- }
-
- return *iter;
-}
-
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/npad.h b/src/core/hle/service/hid/controllers/npad.h
index 5d4c58a43..160dcbbe3 100644
--- a/src/core/hle/service/hid/controllers/npad.h
+++ b/src/core/hle/service/hid/controllers/npad.h
@@ -32,31 +32,37 @@ public:
// When the controller is requesting an update for the shared memory
void OnUpdate(const Core::Timing::CoreTiming& core_timing, u8* data, std::size_t size) override;
+ // When the controller is requesting a motion update for the shared memory
+ void OnMotionUpdate(const Core::Timing::CoreTiming& core_timing, u8* data,
+ std::size_t size) override;
+
// Called when input devices should be loaded
void OnLoadInputDevices() override;
- struct NPadType {
- union {
- u32_le raw{};
-
- BitField<0, 1, u32> pro_controller;
- BitField<1, 1, u32> handheld;
- BitField<2, 1, u32> joycon_dual;
- BitField<3, 1, u32> joycon_left;
- BitField<4, 1, u32> joycon_right;
+ enum class NPadControllerType {
+ None,
+ ProController,
+ Handheld,
+ JoyDual,
+ JoyLeft,
+ JoyRight,
+ Pokeball,
+ };
- BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
- };
+ enum class NpadType : u8 {
+ ProController = 3,
+ Handheld = 4,
+ JoyconDual = 5,
+ JoyconLeft = 6,
+ JoyconRight = 7,
+ Pokeball = 9,
};
- static_assert(sizeof(NPadType) == 4, "NPadType is an invalid size");
- struct Vibration {
- f32 amp_low;
- f32 freq_low;
- f32 amp_high;
- f32 freq_high;
+ enum class DeviceIndex : u8 {
+ Left = 0,
+ Right = 1,
+ None = 2,
};
- static_assert(sizeof(Vibration) == 0x10, "Vibration is an invalid size");
enum class GyroscopeZeroDriftMode : u32 {
Loose = 0,
@@ -69,20 +75,47 @@ public:
Horizontal = 1,
};
- enum class NPadAssignments : u32_le {
+ enum class NpadAssignments : u32 {
Dual = 0,
Single = 1,
};
- enum class NPadControllerType {
- None,
- ProController,
- Handheld,
- JoyDual,
- JoyLeft,
- JoyRight,
- Pokeball,
+ enum class NpadHandheldActivationMode : u64 {
+ Dual = 0,
+ Single = 1,
+ None = 2,
+ };
+
+ struct DeviceHandle {
+ NpadType npad_type{};
+ u8 npad_id{};
+ DeviceIndex device_index{};
+ INSERT_PADDING_BYTES(1);
+ };
+ static_assert(sizeof(DeviceHandle) == 4, "DeviceHandle is an invalid size");
+
+ struct NpadStyleSet {
+ union {
+ u32_le raw{};
+
+ BitField<0, 1, u32> pro_controller;
+ BitField<1, 1, u32> handheld;
+ BitField<2, 1, u32> joycon_dual;
+ BitField<3, 1, u32> joycon_left;
+ BitField<4, 1, u32> joycon_right;
+
+ BitField<6, 1, u32> pokeball; // TODO(ogniK): Confirm when possible
+ };
+ };
+ static_assert(sizeof(NpadStyleSet) == 4, "NpadStyleSet is an invalid size");
+
+ struct VibrationValue {
+ f32 amp_low{0.0f};
+ f32 freq_low{160.0f};
+ f32 amp_high{0.0f};
+ f32 freq_high{320.0f};
};
+ static_assert(sizeof(VibrationValue) == 0x10, "Vibration is an invalid size");
struct LedPattern {
explicit LedPattern(u64 light1, u64 light2, u64 light3, u64 light4) {
@@ -100,39 +133,64 @@ public:
};
};
- void SetSupportedStyleSet(NPadType style_set);
- NPadType GetSupportedStyleSet() const;
+ void SetSupportedStyleSet(NpadStyleSet style_set);
+ NpadStyleSet GetSupportedStyleSet() const;
- void SetSupportedNPadIdTypes(u8* data, std::size_t length);
+ void SetSupportedNpadIdTypes(u8* data, std::size_t length);
void GetSupportedNpadIdTypes(u32* data, std::size_t max_length);
- std::size_t GetSupportedNPadIdTypesSize() const;
+ std::size_t GetSupportedNpadIdTypesSize() const;
void SetHoldType(NpadHoldType joy_hold_type);
NpadHoldType GetHoldType() const;
- void SetNpadMode(u32 npad_id, NPadAssignments assignment_mode);
+ void SetNpadHandheldActivationMode(NpadHandheldActivationMode activation_mode);
+ NpadHandheldActivationMode GetNpadHandheldActivationMode() const;
- void VibrateController(const std::vector<u32>& controller_ids,
- const std::vector<Vibration>& vibrations);
+ void SetNpadMode(u32 npad_id, NpadAssignments assignment_mode);
+
+ bool VibrateControllerAtIndex(std::size_t npad_index, std::size_t device_index,
+ const VibrationValue& vibration_value);
+
+ void VibrateController(const DeviceHandle& vibration_device_handle,
+ const VibrationValue& vibration_value);
+
+ void VibrateControllers(const std::vector<DeviceHandle>& vibration_device_handles,
+ const std::vector<VibrationValue>& vibration_values);
+
+ VibrationValue GetLastVibration(const DeviceHandle& vibration_device_handle) const;
+
+ void InitializeVibrationDevice(const DeviceHandle& vibration_device_handle);
+
+ void InitializeVibrationDeviceAtIndex(std::size_t npad_index, std::size_t device_index);
+
+ void SetPermitVibrationSession(bool permit_vibration_session);
+
+ bool IsVibrationDeviceMounted(const DeviceHandle& vibration_device_handle) const;
std::shared_ptr<Kernel::ReadableEvent> GetStyleSetChangedEvent(u32 npad_id) const;
- Vibration GetLastVibration() const;
+ void SignalStyleSetChangedEvent(u32 npad_id) const;
- void AddNewController(NPadControllerType controller);
- void AddNewControllerAt(NPadControllerType controller, u32 npad_id);
+ // Adds a new controller at an index.
+ void AddNewControllerAt(NPadControllerType controller, std::size_t npad_index);
+ // Adds a new controller at an index with connection status.
+ void UpdateControllerAt(NPadControllerType controller, std::size_t npad_index, bool connected);
+
+ void DisconnectNpad(u32 npad_id);
+ void DisconnectNpadAtIndex(std::size_t index);
- void ConnectNPad(u32 npad_id);
- void DisconnectNPad(u32 npad_id);
void SetGyroscopeZeroDriftMode(GyroscopeZeroDriftMode drift_mode);
GyroscopeZeroDriftMode GetGyroscopeZeroDriftMode() const;
+ bool IsSixAxisSensorAtRest() const;
+ void SetSixAxisEnabled(bool six_axis_status);
LedPattern GetLedPattern(u32 npad_id);
- void SetVibrationEnabled(bool can_vibrate);
- bool IsVibrationEnabled() const;
+ bool IsUnintendedHomeButtonInputProtectionEnabled(u32 npad_id) const;
+ void SetUnintendedHomeButtonInputProtectionEnabled(bool is_protection_enabled, u32 npad_id);
void ClearAllConnectedControllers();
void DisconnectAllConnectedControllers();
void ConnectAllDisconnectedControllers();
void ClearAllControllers();
+ void MergeSingleJoyAsDualJoy(u32 npad_id_1, u32 npad_id_2);
void StartLRAssignmentMode();
void StopLRAssignmentMode();
bool SwapNpadAssignment(u32 npad_id_1, u32 npad_id_2);
@@ -141,6 +199,8 @@ public:
// Specifically for cheat engine and other features.
u32 GetAndResetPressState();
+ static Controller_NPad::NPadControllerType MapSettingsTypeToNPad(Settings::ControllerType type);
+ static Settings::ControllerType MapNPadToSettingsType(Controller_NPad::NPadControllerType type);
static std::size_t NPadIdToIndex(u32 npad_id);
static u32 IndexToNPad(std::size_t index);
@@ -244,6 +304,24 @@ private:
};
static_assert(sizeof(NPadGeneric) == 0x350, "NPadGeneric is an invalid size");
+ struct SixAxisStates {
+ s64_le timestamp{};
+ INSERT_PADDING_WORDS(2);
+ s64_le timestamp2{};
+ Common::Vec3f accel{};
+ Common::Vec3f gyro{};
+ Common::Vec3f rotation{};
+ std::array<Common::Vec3f, 3> orientation{};
+ s64_le always_one{1};
+ };
+ static_assert(sizeof(SixAxisStates) == 0x68, "SixAxisStates is an invalid size");
+
+ struct SixAxisGeneric {
+ CommonHeader common{};
+ std::array<SixAxisStates, 17> sixaxis{};
+ };
+ static_assert(sizeof(SixAxisGeneric) == 0x708, "SixAxisGeneric is an invalid size");
+
enum class ColorReadError : u32_le {
ReadOk = 0,
ColorDoesntExist = 1,
@@ -273,9 +351,16 @@ private:
};
};
+ struct MotionDevice {
+ Common::Vec3f accel;
+ Common::Vec3f gyro;
+ Common::Vec3f rotation;
+ std::array<Common::Vec3f, 3> orientation;
+ };
+
struct NPadEntry {
- NPadType joy_styles;
- NPadAssignments pad_assignment;
+ NpadStyleSet joy_styles;
+ NpadAssignments pad_assignment;
ColorReadError single_color_error;
ControllerColor single_color;
@@ -292,9 +377,12 @@ private:
NPadGeneric pokeball_states;
NPadGeneric libnx; // TODO(ogniK): Find out what this actually is, libnx seems to only be
// relying on this for the time being
- INSERT_PADDING_BYTES(
- 0x708 *
- 6); // TODO(ogniK): SixAxis states, require more information before implementation
+ SixAxisGeneric sixaxis_full;
+ SixAxisGeneric sixaxis_handheld;
+ SixAxisGeneric sixaxis_dual_left;
+ SixAxisGeneric sixaxis_dual_right;
+ SixAxisGeneric sixaxis_left;
+ SixAxisGeneric sixaxis_right;
NPadDevice device_type;
NPadProperties properties;
INSERT_PADDING_WORDS(1);
@@ -309,31 +397,44 @@ private:
bool is_connected;
};
- void InitNewlyAddedControler(std::size_t controller_idx);
+ void InitNewlyAddedController(std::size_t controller_idx);
bool IsControllerSupported(NPadControllerType controller) const;
- NPadControllerType DecideBestController(NPadControllerType priority) const;
void RequestPadStateUpdate(u32 npad_id);
u32 press_state{};
- NPadType style{};
+ NpadStyleSet style{};
std::array<NPadEntry, 10> shared_memory_entries{};
- std::array<
+ using ButtonArray = std::array<
std::array<std::unique_ptr<Input::ButtonDevice>, Settings::NativeButton::NUM_BUTTONS_HID>,
- 10>
- buttons;
- std::array<
+ 10>;
+ using StickArray = std::array<
std::array<std::unique_ptr<Input::AnalogDevice>, Settings::NativeAnalog::NUM_STICKS_HID>,
- 10>
- sticks;
+ 10>;
+ using VibrationArray = std::array<std::array<std::unique_ptr<Input::VibrationDevice>,
+ Settings::NativeVibration::NUM_VIBRATIONS_HID>,
+ 10>;
+ using MotionArray = std::array<
+ std::array<std::unique_ptr<Input::MotionDevice>, Settings::NativeMotion::NUM_MOTIONS_HID>,
+ 10>;
+ ButtonArray buttons;
+ StickArray sticks;
+ VibrationArray vibrations;
+ MotionArray motions;
std::vector<u32> supported_npad_id_types{};
NpadHoldType hold_type{NpadHoldType::Vertical};
+ NpadHandheldActivationMode handheld_activation_mode{NpadHandheldActivationMode::Dual};
// Each controller should have their own styleset changed event
std::array<Kernel::EventPair, 10> styleset_changed_events;
- Vibration last_processed_vibration{};
+ std::array<std::array<std::chrono::steady_clock::time_point, 2>, 10> last_vibration_timepoints;
+ std::array<std::array<VibrationValue, 2>, 10> latest_vibration_values{};
+ bool permit_vibration_session_enabled{false};
+ std::array<std::array<bool, 2>, 10> vibration_devices_mounted{};
std::array<ControllerHolder, 10> connected_controllers{};
+ std::array<bool, 10> unintended_home_button_input_protection{};
GyroscopeZeroDriftMode gyroscope_zero_drift_mode{GyroscopeZeroDriftMode::Standard};
- bool can_controllers_vibrate{true};
+ bool sixaxis_sensors_enabled{true};
+ bool sixaxis_at_rest{true};
std::array<ControllerPad, 10> npad_pad_states{};
bool is_in_lr_assignment_mode{false};
Core::System& system;
diff --git a/src/core/hle/service/hid/controllers/touchscreen.cpp b/src/core/hle/service/hid/controllers/touchscreen.cpp
index e326f8f5c..0df395e85 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.cpp
+++ b/src/core/hle/service/hid/controllers/touchscreen.cpp
@@ -40,9 +40,14 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
cur_entry.sampling_number = last_entry.sampling_number + 1;
cur_entry.sampling_number2 = cur_entry.sampling_number;
- const auto [x, y, pressed] = touch_device->GetStatus();
+ bool pressed = false;
+ float x, y;
+ std::tie(x, y, pressed) = touch_device->GetStatus();
auto& touch_entry = cur_entry.states[0];
touch_entry.attribute.raw = 0;
+ if (!pressed && touch_btn_device) {
+ std::tie(x, y, pressed) = touch_btn_device->GetStatus();
+ }
if (pressed && Settings::values.touchscreen.enabled) {
touch_entry.x = static_cast<u16>(x * Layout::ScreenUndocked::Width);
touch_entry.y = static_cast<u16>(y * Layout::ScreenUndocked::Height);
@@ -63,5 +68,10 @@ void Controller_Touchscreen::OnUpdate(const Core::Timing::CoreTiming& core_timin
void Controller_Touchscreen::OnLoadInputDevices() {
touch_device = Input::CreateDevice<Input::TouchDevice>(Settings::values.touchscreen.device);
+ if (Settings::values.use_touch_from_button) {
+ touch_btn_device = Input::CreateDevice<Input::TouchDevice>("engine:touch_from_button");
+ } else {
+ touch_btn_device.reset();
+ }
}
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/controllers/touchscreen.h b/src/core/hle/service/hid/controllers/touchscreen.h
index a1d97269e..4d9042adc 100644
--- a/src/core/hle/service/hid/controllers/touchscreen.h
+++ b/src/core/hle/service/hid/controllers/touchscreen.h
@@ -68,6 +68,7 @@ private:
"TouchScreenSharedMemory is an invalid size");
TouchScreenSharedMemory shared_memory{};
std::unique_ptr<Input::TouchDevice> touch_device;
+ std::unique_ptr<Input::TouchDevice> touch_btn_device;
s64_le last_touch{};
};
} // namespace Service::HID
diff --git a/src/core/hle/service/hid/hid.cpp b/src/core/hle/service/hid/hid.cpp
index 1e95b7580..902516b29 100644
--- a/src/core/hle/service/hid/hid.cpp
+++ b/src/core/hle/service/hid/hid.cpp
@@ -38,11 +38,10 @@
namespace Service::HID {
// Updating period for each HID device.
-// TODO(ogniK): Find actual polling rate of hid
-constexpr auto pad_update_ns = std::chrono::nanoseconds{1000000000 / 66};
-[[maybe_unused]] constexpr auto accelerometer_update_ns =
- std::chrono::nanoseconds{1000000000 / 100};
-[[maybe_unused]] constexpr auto gyroscope_update_ticks = std::chrono::nanoseconds{1000000000 / 100};
+// HID is polled every 15ms, this value was derived from
+// https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering#joy-con-status-data-packet
+constexpr auto pad_update_ns = std::chrono::nanoseconds{1000 * 1000}; // (1ms, 1000Hz)
+constexpr auto motion_update_ns = std::chrono::nanoseconds{15 * 1000 * 1000}; // (15ms, 66.666Hz)
constexpr std::size_t SHARED_MEMORY_SIZE = 0x40000;
IAppletResource::IAppletResource(Core::System& system)
@@ -81,10 +80,14 @@ IAppletResource::IAppletResource(Core::System& system)
[this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
UpdateControllers(user_data, ns_late);
});
-
- // TODO(shinyquagsire23): Other update callbacks? (accel, gyro?)
+ motion_update_event = Core::Timing::CreateEvent(
+ "HID::MotionPadCallback",
+ [this](std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ UpdateMotion(user_data, ns_late);
+ });
system.CoreTiming().ScheduleEvent(pad_update_ns, pad_update_event);
+ system.CoreTiming().ScheduleEvent(motion_update_ns, motion_update_event);
ReloadInputDevices();
}
@@ -124,22 +127,46 @@ void IAppletResource::UpdateControllers(std::uintptr_t user_data,
core_timing.ScheduleEvent(pad_update_ns - ns_late, pad_update_event);
}
+void IAppletResource::UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late) {
+ auto& core_timing = system.CoreTiming();
+
+ for (const auto& controller : controllers) {
+ controller->OnMotionUpdate(core_timing, shared_mem->GetPointer(), SHARED_MEMORY_SIZE);
+ }
+
+ core_timing.ScheduleEvent(motion_update_ns - ns_late, motion_update_event);
+}
+
class IActiveVibrationDeviceList final : public ServiceFramework<IActiveVibrationDeviceList> {
public:
- IActiveVibrationDeviceList() : ServiceFramework("IActiveVibrationDeviceList") {
+ explicit IActiveVibrationDeviceList(std::shared_ptr<IAppletResource> applet_resource_)
+ : ServiceFramework("IActiveVibrationDeviceList"), applet_resource(applet_resource_) {
+ // clang-format off
static const FunctionInfo functions[] = {
- {0, &IActiveVibrationDeviceList::ActivateVibrationDevice, "ActivateVibrationDevice"},
+ {0, &IActiveVibrationDeviceList::InitializeVibrationDevice, "InitializeVibrationDevice"},
};
+ // clang-format on
+
RegisterHandlers(functions);
}
private:
- void ActivateVibrationDevice(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service_HID, "(STUBBED) called");
+ void InitializeVibrationDevice(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .InitializeVibrationDevice(vibration_device_handle);
+
+ LOG_DEBUG(Service_HID, "called, npad_type={}, npad_id={}, device_index={}",
+ vibration_device_handle.npad_type, vibration_device_handle.npad_id,
+ vibration_device_handle.device_index);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+
+ std::shared_ptr<IAppletResource> applet_resource;
};
std::shared_ptr<IAppletResource> Hid::GetAppletResource() {
@@ -166,8 +193,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{56, nullptr, "ActivateJoyXpad"},
{58, nullptr, "GetJoyXpadLifoHandle"},
{59, nullptr, "GetJoyXpadIds"},
- {60, nullptr, "ActivateSixAxisSensor"},
- {61, nullptr, "DeactivateSixAxisSensor"},
+ {60, &Hid::ActivateSixAxisSensor, "ActivateSixAxisSensor"},
+ {61, &Hid::DeactivateSixAxisSensor, "DeactivateSixAxisSensor"},
{62, nullptr, "GetSixAxisSensorLifoHandle"},
{63, nullptr, "ActivateJoySixAxisSensor"},
{64, nullptr, "DeactivateJoySixAxisSensor"},
@@ -175,7 +202,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{66, &Hid::StartSixAxisSensor, "StartSixAxisSensor"},
{67, &Hid::StopSixAxisSensor, "StopSixAxisSensor"},
{68, nullptr, "IsSixAxisSensorFusionEnabled"},
- {69, nullptr, "EnableSixAxisSensorFusion"},
+ {69, &Hid::EnableSixAxisSensorFusion, "EnableSixAxisSensorFusion"},
{70, nullptr, "SetSixAxisSensorFusionParameters"},
{71, nullptr, "GetSixAxisSensorFusionParameters"},
{72, nullptr, "ResetSixAxisSensorFusionParameters"},
@@ -211,8 +238,8 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{128, &Hid::SetNpadHandheldActivationMode, "SetNpadHandheldActivationMode"},
{129, &Hid::GetNpadHandheldActivationMode, "GetNpadHandheldActivationMode"},
{130, &Hid::SwapNpadAssignment, "SwapNpadAssignment"},
- {131, nullptr, "IsUnintendedHomeButtonInputProtectionEnabled"},
- {132, nullptr, "EnableUnintendedHomeButtonInputProtection"},
+ {131, &Hid::IsUnintendedHomeButtonInputProtectionEnabled, "IsUnintendedHomeButtonInputProtectionEnabled"},
+ {132, &Hid::EnableUnintendedHomeButtonInputProtection, "EnableUnintendedHomeButtonInputProtection"},
{133, nullptr, "SetNpadJoyAssignmentModeSingleWithDestination"},
{134, nullptr, "SetNpadAnalogStickUseCenterClamp"},
{135, nullptr, "SetNpadCaptureButtonAssignment"},
@@ -228,7 +255,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{208, nullptr, "GetActualVibrationGcErmCommand"},
{209, &Hid::BeginPermitVibrationSession, "BeginPermitVibrationSession"},
{210, &Hid::EndPermitVibrationSession, "EndPermitVibrationSession"},
- {211, nullptr, "IsVibrationDeviceMounted"},
+ {211, &Hid::IsVibrationDeviceMounted, "IsVibrationDeviceMounted"},
{300, &Hid::ActivateConsoleSixAxisSensor, "ActivateConsoleSixAxisSensor"},
{301, &Hid::StartConsoleSixAxisSensor, "StartConsoleSixAxisSensor"},
{302, &Hid::StopConsoleSixAxisSensor, "StopConsoleSixAxisSensor"},
@@ -247,7 +274,7 @@ Hid::Hid(Core::System& system) : ServiceFramework("hid"), system(system) {
{404, nullptr, "HasLeftRightBattery"},
{405, nullptr, "GetNpadInterfaceType"},
{406, nullptr, "GetNpadLeftRightInterfaceType"},
- {407, nullptr, "GetNpadOfHighestBatteryLevelForJoyLeft"},
+ {407, nullptr, "GetNpadOfHighestBatteryLevel"},
{408, nullptr, "GetNpadOfHighestBatteryLevelForJoyRight"},
{500, nullptr, "GetPalmaConnectionHandle"},
{501, nullptr, "InitializePalma"},
@@ -307,117 +334,152 @@ void Hid::CreateAppletResource(Kernel::HLERequestContext& ctx) {
rb.PushIpcInterface<IAppletResource>(applet_resource);
}
-void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto basic_xpad_id{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}", basic_xpad_id,
- applet_resource_user_id);
+ applet_resource->ActivateController(HidController::DebugPad);
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::XPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
+ applet_resource->ActivateController(HidController::Touchscreen);
- IPC::ResponseBuilder rb{ctx, 3};
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- rb.Push(0);
}
-void Hid::ActivateDebugPad(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->ActivateController(HidController::Mouse);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::DebugPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateTouchScreen(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->ActivateController(HidController::Keyboard);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->ActivateController(HidController::Touchscreen);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateMouse(Kernel::HLERequestContext& ctx) {
+void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto flags{rp.Pop<u32>()};
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
- applet_resource->ActivateController(HidController::Mouse);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateKeyboard(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateXpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 basic_xpad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->ActivateController(HidController::XPad);
+
+ LOG_DEBUG(Service_HID, "called, basic_xpad_id={}, applet_resource_user_id={}",
+ parameters.basic_xpad_id, parameters.applet_resource_user_id);
- applet_resource->ActivateController(HidController::Keyboard);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx) {
+void Hid::GetXpadIDs(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto flags{rp.Pop<u32>()};
- LOG_WARNING(Service_HID, "(STUBBED) called. flags={}", flags);
+ const auto applet_resource_user_id{rp.Pop<u64>()};
- IPC::ResponseBuilder rb{ctx, 2};
+ LOG_DEBUG(Service_HID, "(STUBBED) called, applet_resource_user_id={}", applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
+ rb.Push(0);
}
-void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
+void Hid::ActivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto unknown{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
- applet_resource->ActivateController(HidController::Gesture);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
-void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
- // Should have no effect with how our npad sets up the data
+void Hid::DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto unknown{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", unknown,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
- applet_resource->ActivateController(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(true);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -425,11 +487,42 @@ void Hid::StartSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetSixAxisEnabled(false);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ bool enable_sixaxis_sensor_fusion{};
+ INSERT_PADDING_BYTES(3);
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, enable_sixaxis_sensor_fusion={}, npad_type={}, npad_id={}, "
+ "device_index={}, applet_resource_user_id={}",
+ parameters.enable_sixaxis_sensor_fusion, parameters.sixaxis_handle.npad_type,
+ parameters.sixaxis_handle.npad_id, parameters.sixaxis_handle.device_index,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -437,14 +530,17 @@ void Hid::StopSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto drift_mode{rp.Pop<u32>()};
+ const auto sixaxis_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+ const auto drift_mode{rp.PopEnum<Controller_NPad::GyroscopeZeroDriftMode>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode{drift_mode});
+ .SetGyroscopeZeroDriftMode(drift_mode);
- LOG_DEBUG(Service_HID, "called, handle={}, drift_mode={}, applet_resource_user_id={}", handle,
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, drift_mode={}, "
+ "applet_resource_user_id={}",
+ sixaxis_handle.npad_type, sixaxis_handle.npad_id, sixaxis_handle.device_index,
drift_mode, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
@@ -453,29 +549,42 @@ void Hid::SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
- LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(
- static_cast<u32>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetGyroscopeZeroDriftMode()));
+ rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetGyroscopeZeroDriftMode());
}
void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetGyroscopeZeroDriftMode(Controller_NPad::GyroscopeZeroDriftMode::Standard);
- LOG_DEBUG(Service_HID, "called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -483,27 +592,53 @@ void Hid::ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx) {
void Hid::IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- // TODO (Hexagon12): Properly implement reading gyroscope values from controllers.
- rb.Push(true);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .IsSixAxisSensorAtRest());
+}
+
+void Hid::ActivateGesture(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ u32 unknown{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->ActivateController(HidController::Gesture);
+
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
+ parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
void Hid::SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto supported_styleset{rp.Pop<u32>()};
- LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
-
applet_resource->GetController<Controller_NPad>(HidController::NPad)
.SetSupportedStyleSet({supported_styleset});
+ LOG_DEBUG(Service_HID, "called, supported_styleset={}", supported_styleset);
+
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -514,21 +649,22 @@ void Hid::GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
-
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(controller.GetSupportedStyleSet().raw);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetSupportedStyleSet()
+ .raw);
}
void Hid::SetSupportedNpadIdType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetSupportedNpadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetSupportedNPadIdTypes(ctx.ReadBuffer().data(), ctx.GetReadBufferSize());
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -537,48 +673,62 @@ void Hid::ActivateNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->ActivateController(HidController::NPad);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- applet_resource->ActivateController(HidController::NPad);
}
void Hid::DeactivateNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->DeactivateController(HidController::NPad);
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
- applet_resource->DeactivateController(HidController::NPad);
}
void Hid::AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto unknown{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ u64 unknown{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
- LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}", npad_id,
- applet_resource_user_id, unknown);
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}, unknown={}",
+ parameters.npad_id, parameters.applet_resource_user_id, parameters.unknown);
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
rb.PushCopyObjects(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetStyleSetChangedEvent(npad_id));
+ .GetStyleSetChangedEvent(parameters.npad_id));
}
void Hid::DisconnectNpad(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .DisconnectNpad(parameters.npad_id);
- LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
- applet_resource_user_id);
+ LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", parameters.npad_id,
+ parameters.applet_resource_user_id);
- applet_resource->GetController<Controller_NPad>(HidController::NPad).DisconnectNPad(npad_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -591,22 +741,41 @@ void Hid::GetPlayerLedPattern(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.PushRaw<u64>(applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .GetLedPattern(npad_id)
- .raw);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetLedPattern(npad_id)
+ .raw);
+}
+
+void Hid::ActivateNpadWithRevision(Kernel::HLERequestContext& ctx) {
+ // Should have no effect with how our npad sets up the data
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ u32 unknown{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->ActivateController(HidController::NPad);
+
+ LOG_DEBUG(Service_HID, "called, unknown={}, applet_resource_user_id={}", parameters.unknown,
+ parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
}
void Hid::SetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto hold_type{rp.Pop<u64>()};
+ const auto hold_type{rp.PopEnum<Controller_NPad::NpadHoldType>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).SetHoldType(hold_type);
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, hold_type={}",
applet_resource_user_id, hold_type);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetHoldType(Controller_NPad::NpadHoldType{hold_type});
-
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
@@ -617,22 +786,26 @@ void Hid::GetNpadJoyHoldType(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- const auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u64>(static_cast<u64>(controller.GetHoldType()));
+ rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad).GetHoldType());
}
void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}", npad_id,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
+
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -641,16 +814,22 @@ void Hid::SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx
void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
// TODO: Check the differences between this and SetNpadJoyAssignmentModeSingleByDefault
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto npad_joy_device_type{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ u64 npad_joy_device_type{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Single);
LOG_WARNING(Service_HID,
"(STUBBED) called, npad_id={}, applet_resource_user_id={}, npad_joy_device_type={}",
- npad_id, applet_resource_user_id, npad_joy_device_type);
-
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Single);
+ parameters.npad_id, parameters.applet_resource_user_id,
+ parameters.npad_joy_device_type);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -658,14 +837,19 @@ void Hid::SetNpadJoyAssignmentModeSingle(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, npad_id={}, applet_resource_user_id={}", npad_id,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadMode(parameters.npad_id, Controller_NPad::NpadAssignments::Dual);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.SetNpadMode(npad_id, Controller_NPad::NPadAssignments::Dual);
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -673,13 +857,15 @@ void Hid::SetNpadJoyAssignmentModeDual(Kernel::HLERequestContext& ctx) {
void Hid::MergeSingleJoyAsDualJoy(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto unknown_1{rp.Pop<u32>()};
- const auto unknown_2{rp.Pop<u32>()};
+ const auto npad_id_1{rp.Pop<u32>()};
+ const auto npad_id_2{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID,
- "(STUBBED) called, unknown_1={}, unknown_2={}, applet_resource_user_id={}",
- unknown_1, unknown_2, applet_resource_user_id);
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .MergeSingleJoyAsDualJoy(npad_id_1, npad_id_2);
+
+ LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
+ npad_id_1, npad_id_2, applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -689,9 +875,9 @@ void Hid::StartLrAssignmentMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).StartLRAssignmentMode();
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.StartLRAssignmentMode();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -701,9 +887,9 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
+ applet_resource->GetController<Controller_NPad>(HidController::NPad).StopLRAssignmentMode();
+
LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
- controller.StopLRAssignmentMode();
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -712,10 +898,13 @@ void Hid::StopLrAssignmentMode(Kernel::HLERequestContext& ctx) {
void Hid::SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- const auto mode{rp.Pop<u64>()};
+ const auto activation_mode{rp.PopEnum<Controller_NPad::NpadHandheldActivationMode>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetNpadHandheldActivationMode(activation_mode);
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}, mode={}",
- applet_resource_user_id, mode);
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, activation_mode={}",
+ applet_resource_user_id, activation_mode);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -725,25 +914,28 @@ void Hid::GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_WARNING(Service_HID, "(STUBBED) called, applet_resource_user_id={}",
- applet_resource_user_id);
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetNpadHandheldActivationMode());
}
void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto npad_1{rp.Pop<u32>()};
- const auto npad_2{rp.Pop<u32>()};
+ const auto npad_id_1{rp.Pop<u32>()};
+ const auto npad_id_2{rp.Pop<u32>()};
const auto applet_resource_user_id{rp.Pop<u64>()};
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}, npad_1={}, npad_2={}",
- applet_resource_user_id, npad_1, npad_2);
+ const bool res = applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SwapNpadAssignment(npad_id_1, npad_id_2);
+
+ LOG_DEBUG(Service_HID, "called, npad_id_1={}, npad_id_2={}, applet_resource_user_id={}",
+ npad_id_1, npad_id_2, applet_resource_user_id);
- auto& controller = applet_resource->GetController<Controller_NPad>(HidController::NPad);
IPC::ResponseBuilder rb{ctx, 2};
- if (controller.SwapNpadAssignment(npad_1, npad_2)) {
+ if (res) {
rb.Push(RESULT_SUCCESS);
} else {
LOG_ERROR(Service_HID, "Npads are not connected!");
@@ -751,61 +943,99 @@ void Hid::SwapNpadAssignment(Kernel::HLERequestContext& ctx) {
}
}
-void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+void Hid::IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ u32 npad_id{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(true);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(RESULT_SUCCESS);
-}
-
-void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ LOG_WARNING(Service_HID, "(STUBBED) called, npad_id={}, applet_resource_user_id={}",
+ parameters.npad_id, parameters.applet_resource_user_id);
- applet_resource->GetController<Controller_NPad>(HidController::NPad).SetVibrationEnabled(false);
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .IsUnintendedHomeButtonInputProtectionEnabled(parameters.npad_id));
}
-void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
+void Hid::EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto controller_id{rp.Pop<u32>()};
- const auto vibration_values{rp.PopRaw<Controller_NPad::Vibration>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ bool unintended_home_button_input_protection{};
+ INSERT_PADDING_BYTES(3);
+ u32 npad_id{};
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetUnintendedHomeButtonInputProtectionEnabled(
+ parameters.unintended_home_button_input_protection, parameters.npad_id);
- LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id,
- applet_resource_user_id);
+ LOG_WARNING(Service_HID,
+ "(STUBBED) called, unintended_home_button_input_protection={}, npad_id={},"
+ "applet_resource_user_id={}",
+ parameters.unintended_home_button_input_protection, parameters.npad_id,
+ parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
-
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .VibrateController({controller_id}, {vibration_values});
}
-void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
+void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ const auto vibration_device_handle{rp.PopRaw<Controller_NPad::DeviceHandle>()};
+
+ VibrationDeviceInfo vibration_device_info;
+
+ vibration_device_info.type = VibrationDeviceType::LinearResonantActuator;
+
+ switch (vibration_device_handle.device_index) {
+ case Controller_NPad::DeviceIndex::Left:
+ vibration_device_info.position = VibrationDevicePosition::Left;
+ break;
+ case Controller_NPad::DeviceIndex::Right:
+ vibration_device_info.position = VibrationDevicePosition::Right;
+ break;
+ case Controller_NPad::DeviceIndex::None:
+ default:
+ UNREACHABLE_MSG("DeviceIndex should never be None!");
+ vibration_device_info.position = VibrationDevicePosition::None;
+ break;
+ }
- LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+ LOG_DEBUG(Service_HID, "called, vibration_device_type={}, vibration_device_position={}",
+ vibration_device_info.type, vibration_device_info.position);
- const auto controllers = ctx.ReadBuffer(0);
- const auto vibrations = ctx.ReadBuffer(1);
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(vibration_device_info);
+}
- std::vector<u32> controller_list(controllers.size() / sizeof(u32));
- std::vector<Controller_NPad::Vibration> vibration_list(vibrations.size() /
- sizeof(Controller_NPad::Vibration));
+void Hid::SendVibrationValue(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle vibration_device_handle{};
+ Controller_NPad::VibrationValue vibration_value{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- std::memcpy(controller_list.data(), controllers.data(), controllers.size());
- std::memcpy(vibration_list.data(), vibrations.data(), vibrations.size());
- std::transform(controller_list.begin(), controller_list.end(), controller_list.begin(),
- [](u32 controller_id) { return controller_id - 3; });
+ const auto parameters{rp.PopRaw<Parameters>()};
applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .VibrateController(controller_list, vibration_list);
+ .VibrateController(parameters.vibration_device_handle, parameters.vibration_value);
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.vibration_device_handle.npad_type,
+ parameters.vibration_device_handle.npad_id,
+ parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -813,25 +1043,24 @@ void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
void Hid::GetActualVibrationValue(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto controller_id{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle vibration_device_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_DEBUG(Service_HID, "called, controller_id={}, applet_resource_user_id={}", controller_id,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
- IPC::ResponseBuilder rb{ctx, 6};
- rb.Push(RESULT_SUCCESS);
- rb.PushRaw<Controller_NPad::Vibration>(
- applet_resource->GetController<Controller_NPad>(HidController::NPad).GetLastVibration());
-}
-
-void Hid::GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_HID, "called");
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.vibration_device_handle.npad_type,
+ parameters.vibration_device_handle.npad_id,
+ parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
- IPC::ResponseBuilder rb{ctx, 4};
+ IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(1);
- rb.Push<u32>(0);
+ rb.PushRaw(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .GetLastVibration(parameters.vibration_device_handle));
}
void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
@@ -839,14 +1068,14 @@ void Hid::CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<IActiveVibrationDeviceList>();
+ rb.PushIpcInterface<IActiveVibrationDeviceList>(applet_resource);
}
void Hid::PermitVibration(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
const auto can_vibrate{rp.Pop<bool>()};
- applet_resource->GetController<Controller_NPad>(HidController::NPad)
- .SetVibrationEnabled(can_vibrate);
+
+ Settings::values.vibration_enabled.SetValue(can_vibrate);
LOG_DEBUG(Service_HID, "called, can_vibrate={}", can_vibrate);
@@ -859,8 +1088,76 @@ void Hid::IsVibrationPermitted(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(
- applet_resource->GetController<Controller_NPad>(HidController::NPad).IsVibrationEnabled());
+ rb.Push(Settings::values.vibration_enabled.GetValue());
+}
+
+void Hid::SendVibrationValues(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ const auto handles = ctx.ReadBuffer(0);
+ const auto vibrations = ctx.ReadBuffer(1);
+
+ std::vector<Controller_NPad::DeviceHandle> vibration_device_handles(
+ handles.size() / sizeof(Controller_NPad::DeviceHandle));
+ std::vector<Controller_NPad::VibrationValue> vibration_values(
+ vibrations.size() / sizeof(Controller_NPad::VibrationValue));
+
+ std::memcpy(vibration_device_handles.data(), handles.data(), handles.size());
+ std::memcpy(vibration_values.data(), vibrations.data(), vibrations.size());
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .VibrateControllers(vibration_device_handles, vibration_values);
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::BeginPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto applet_resource_user_id{rp.Pop<u64>()};
+
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetPermitVibrationSession(true);
+
+ LOG_DEBUG(Service_HID, "called, applet_resource_user_id={}", applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::EndPermitVibrationSession(Kernel::HLERequestContext& ctx) {
+ applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .SetPermitVibrationSession(false);
+
+ LOG_DEBUG(Service_HID, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+}
+
+void Hid::IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ struct Parameters {
+ Controller_NPad::DeviceHandle vibration_device_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
+
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_DEBUG(Service_HID,
+ "called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.vibration_device_handle.npad_type,
+ parameters.vibration_device_handle.npad_id,
+ parameters.vibration_device_handle.device_index, parameters.applet_resource_user_id);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push(applet_resource->GetController<Controller_NPad>(HidController::NPad)
+ .IsVibrationDeviceMounted(parameters.vibration_device_handle));
}
void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
@@ -876,11 +1173,19 @@ void Hid::ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
@@ -888,11 +1193,19 @@ void Hid::StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
void Hid::StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- const auto handle{rp.Pop<u32>()};
- const auto applet_resource_user_id{rp.Pop<u64>()};
+ struct Parameters {
+ Controller_NPad::DeviceHandle sixaxis_handle{};
+ INSERT_PADDING_WORDS(1);
+ u64 applet_resource_user_id{};
+ };
- LOG_WARNING(Service_HID, "(STUBBED) called, handle={}, applet_resource_user_id={}", handle,
- applet_resource_user_id);
+ const auto parameters{rp.PopRaw<Parameters>()};
+
+ LOG_WARNING(
+ Service_HID,
+ "(STUBBED) called, npad_type={}, npad_id={}, device_index={}, applet_resource_user_id={}",
+ parameters.sixaxis_handle.npad_type, parameters.sixaxis_handle.npad_id,
+ parameters.sixaxis_handle.device_index, parameters.applet_resource_user_id);
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
diff --git a/src/core/hle/service/hid/hid.h b/src/core/hle/service/hid/hid.h
index efb07547f..c8e4a4b55 100644
--- a/src/core/hle/service/hid/hid.h
+++ b/src/core/hle/service/hid/hid.h
@@ -65,10 +65,12 @@ private:
void GetSharedMemoryHandle(Kernel::HLERequestContext& ctx);
void UpdateControllers(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
+ void UpdateMotion(std::uintptr_t user_data, std::chrono::nanoseconds ns_late);
std::shared_ptr<Kernel::SharedMemory> shared_mem;
std::shared_ptr<Core::Timing::EventType> pad_update_event;
+ std::shared_ptr<Core::Timing::EventType> motion_update_event;
Core::System& system;
std::array<std::unique_ptr<ControllerBase>, static_cast<size_t>(HidController::MaxControllers)>
@@ -84,21 +86,23 @@ public:
private:
void CreateAppletResource(Kernel::HLERequestContext& ctx);
- void ActivateXpad(Kernel::HLERequestContext& ctx);
- void GetXpadIDs(Kernel::HLERequestContext& ctx);
void ActivateDebugPad(Kernel::HLERequestContext& ctx);
void ActivateTouchScreen(Kernel::HLERequestContext& ctx);
void ActivateMouse(Kernel::HLERequestContext& ctx);
void ActivateKeyboard(Kernel::HLERequestContext& ctx);
void SendKeyboardLockKeyEvent(Kernel::HLERequestContext& ctx);
- void ActivateGesture(Kernel::HLERequestContext& ctx);
- void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
+ void ActivateXpad(Kernel::HLERequestContext& ctx);
+ void GetXpadIDs(Kernel::HLERequestContext& ctx);
+ void ActivateSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void DeactivateSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopSixAxisSensor(Kernel::HLERequestContext& ctx);
+ void EnableSixAxisSensorFusion(Kernel::HLERequestContext& ctx);
void SetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void GetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void ResetGyroscopeZeroDriftMode(Kernel::HLERequestContext& ctx);
void IsSixAxisSensorAtRest(Kernel::HLERequestContext& ctx);
+ void ActivateGesture(Kernel::HLERequestContext& ctx);
void SetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void GetSupportedNpadStyleSet(Kernel::HLERequestContext& ctx);
void SetSupportedNpadIdType(Kernel::HLERequestContext& ctx);
@@ -107,6 +111,7 @@ private:
void AcquireNpadStyleSetUpdateEventHandle(Kernel::HLERequestContext& ctx);
void DisconnectNpad(Kernel::HLERequestContext& ctx);
void GetPlayerLedPattern(Kernel::HLERequestContext& ctx);
+ void ActivateNpadWithRevision(Kernel::HLERequestContext& ctx);
void SetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void GetNpadJoyHoldType(Kernel::HLERequestContext& ctx);
void SetNpadJoyAssignmentModeSingleByDefault(Kernel::HLERequestContext& ctx);
@@ -118,15 +123,18 @@ private:
void SetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void GetNpadHandheldActivationMode(Kernel::HLERequestContext& ctx);
void SwapNpadAssignment(Kernel::HLERequestContext& ctx);
- void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
- void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void IsUnintendedHomeButtonInputProtectionEnabled(Kernel::HLERequestContext& ctx);
+ void EnableUnintendedHomeButtonInputProtection(Kernel::HLERequestContext& ctx);
+ void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void SendVibrationValue(Kernel::HLERequestContext& ctx);
- void SendVibrationValues(Kernel::HLERequestContext& ctx);
void GetActualVibrationValue(Kernel::HLERequestContext& ctx);
- void GetVibrationDeviceInfo(Kernel::HLERequestContext& ctx);
void CreateActiveVibrationDeviceList(Kernel::HLERequestContext& ctx);
void PermitVibration(Kernel::HLERequestContext& ctx);
void IsVibrationPermitted(Kernel::HLERequestContext& ctx);
+ void SendVibrationValues(Kernel::HLERequestContext& ctx);
+ void BeginPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void EndPermitVibrationSession(Kernel::HLERequestContext& ctx);
+ void IsVibrationDeviceMounted(Kernel::HLERequestContext& ctx);
void ActivateConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StartConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
void StopConsoleSixAxisSensor(Kernel::HLERequestContext& ctx);
@@ -139,6 +147,22 @@ private:
void SetIsPalmaAllConnectable(Kernel::HLERequestContext& ctx);
void SetPalmaBoostMode(Kernel::HLERequestContext& ctx);
+ enum class VibrationDeviceType : u32 {
+ LinearResonantActuator = 1,
+ };
+
+ enum class VibrationDevicePosition : u32 {
+ None = 0,
+ Left = 1,
+ Right = 2,
+ };
+
+ struct VibrationDeviceInfo {
+ VibrationDeviceType type{};
+ VibrationDevicePosition position{};
+ };
+ static_assert(sizeof(VibrationDeviceInfo) == 0x8, "VibrationDeviceInfo has incorrect size.");
+
std::shared_ptr<IAppletResource> applet_resource;
Core::System& system;
};
diff --git a/src/core/hle/service/ldr/ldr.cpp b/src/core/hle/service/ldr/ldr.cpp
index d8cd10e31..65c209725 100644
--- a/src/core/hle/service/ldr/ldr.cpp
+++ b/src/core/hle/service/ldr/ldr.cpp
@@ -9,6 +9,7 @@
#include "common/alignment.h"
#include "common/hex_util.h"
#include "common/scope_exit.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/errors.h"
#include "core/hle/kernel/memory/page_table.h"
@@ -23,7 +24,7 @@ namespace Service::LDR {
constexpr ResultCode ERROR_INSUFFICIENT_ADDRESS_SPACE{ErrorModule::RO, 2};
-constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
+[[maybe_unused]] constexpr ResultCode ERROR_INVALID_MEMORY_STATE{ErrorModule::Loader, 51};
constexpr ResultCode ERROR_INVALID_NRO{ErrorModule::Loader, 52};
constexpr ResultCode ERROR_INVALID_NRR{ErrorModule::Loader, 53};
constexpr ResultCode ERROR_MISSING_NRR_HASH{ErrorModule::Loader, 54};
@@ -33,7 +34,7 @@ constexpr ResultCode ERROR_ALREADY_LOADED{ErrorModule::Loader, 57};
constexpr ResultCode ERROR_INVALID_ALIGNMENT{ErrorModule::Loader, 81};
constexpr ResultCode ERROR_INVALID_SIZE{ErrorModule::Loader, 82};
constexpr ResultCode ERROR_INVALID_NRO_ADDRESS{ErrorModule::Loader, 84};
-constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
+[[maybe_unused]] constexpr ResultCode ERROR_INVALID_NRR_ADDRESS{ErrorModule::Loader, 85};
constexpr ResultCode ERROR_NOT_INITIALIZED{ErrorModule::Loader, 87};
constexpr std::size_t MAXIMUM_LOADED_RO{0x40};
@@ -166,7 +167,7 @@ public:
{0, &RelocatableObject::LoadNro, "LoadNro"},
{1, &RelocatableObject::UnloadNro, "UnloadNro"},
{2, &RelocatableObject::LoadNrr, "LoadNrr"},
- {3, nullptr, "UnloadNrr"},
+ {3, &RelocatableObject::UnloadNrr, "UnloadNrr"},
{4, &RelocatableObject::Initialize, "Initialize"},
{10, nullptr, "LoadNrrEx"},
};
@@ -272,6 +273,20 @@ public:
rb.Push(RESULT_SUCCESS);
}
+ void UnloadNrr(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const auto pid = rp.Pop<u64>();
+ const auto nrr_address = rp.Pop<VAddr>();
+
+ LOG_DEBUG(Service_LDR, "called with pid={}, nrr_address={:016X}", pid, nrr_address);
+
+ nrr.erase(nrr_address);
+
+ IPC::ResponseBuilder rb{ctx, 2};
+
+ rb.Push(RESULT_SUCCESS);
+ }
+
bool ValidateRegionForMap(Kernel::Memory::PageTable& page_table, VAddr start,
std::size_t size) const {
constexpr std::size_t padding_size{4 * Kernel::Memory::PageSize};
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 @@
#include "common/logging/log.h"
#include "common/scope_exit.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/service/lm/lm.h"
#include "core/hle/service/lm/manager.h"
diff --git a/src/core/hle/service/mii/manager.cpp b/src/core/hle/service/mii/manager.cpp
index 4730070cb..d73b90015 100644
--- a/src/core/hle/service/mii/manager.cpp
+++ b/src/core/hle/service/mii/manager.cpp
@@ -131,7 +131,7 @@ template <typename T>
T GetRandomValue(T min, T max) {
std::random_device device;
std::mt19937 gen(device());
- std::uniform_int_distribution<u64> distribution(0, static_cast<u64>(max));
+ std::uniform_int_distribution<u64> distribution(static_cast<u64>(min), static_cast<u64>(max));
return static_cast<T>(distribution(gen));
}
@@ -428,7 +428,7 @@ bool MiiManager::IsFullDatabase() const {
}
u32 MiiManager::GetCount(SourceFlag source_flag) const {
- u32 count{};
+ std::size_t count{};
if ((source_flag & SourceFlag::Database) != SourceFlag::None) {
// TODO(bunnei): We don't implement the Mii database, but when we do, update this
count += 0;
@@ -436,7 +436,7 @@ u32 MiiManager::GetCount(SourceFlag source_flag) const {
if ((source_flag & SourceFlag::Default) != SourceFlag::None) {
count += DefaultMiiCount;
}
- return count;
+ return static_cast<u32>(count);
}
ResultVal<MiiInfo> MiiManager::UpdateLatest([[maybe_unused]] const MiiInfo& info,
diff --git a/src/core/hle/service/mii/mii.cpp b/src/core/hle/service/mii/mii.cpp
index b81bf6277..d7080b715 100644
--- a/src/core/hle/service/mii/mii.cpp
+++ b/src/core/hle/service/mii/mii.cpp
@@ -47,6 +47,7 @@ public:
{23, nullptr, "Convert"},
{24, nullptr, "ConvertCoreDataToCharInfo"},
{25, nullptr, "ConvertCharInfoToCoreData"},
+ {26, nullptr, "Append"},
};
// clang-format on
diff --git a/src/core/hle/service/nfp/nfp.cpp b/src/core/hle/service/nfp/nfp.cpp
index 5e2d769a4..a0469ffbd 100644
--- a/src/core/hle/service/nfp/nfp.cpp
+++ b/src/core/hle/service/nfp/nfp.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
#include <atomic>
#include "common/logging/log.h"
@@ -72,10 +73,10 @@ private:
std::array<u8, 10> uuid;
u8 uuid_length; // TODO(ogniK): Figure out if this is actual the uuid length or does it
// mean something else
- INSERT_PADDING_BYTES(0x15);
+ std::array<u8, 0x15> padding_1;
u32_le protocol;
u32_le tag_type;
- INSERT_PADDING_BYTES(0x2c);
+ std::array<u8, 0x2c> padding_2;
};
static_assert(sizeof(TagInfo) == 0x54, "TagInfo is an invalid size");
@@ -213,13 +214,15 @@ private:
LOG_DEBUG(Service_NFP, "called");
IPC::ResponseBuilder rb{ctx, 2};
- auto amiibo = nfp_interface.GetAmiiboBuffer();
- TagInfo tag_info{};
- tag_info.uuid = amiibo.uuid;
- tag_info.uuid_length = static_cast<u8>(tag_info.uuid.size());
-
- tag_info.protocol = 1; // TODO(ogniK): Figure out actual values
- tag_info.tag_type = 2;
+ const auto& amiibo = nfp_interface.GetAmiiboBuffer();
+ const TagInfo tag_info{
+ .uuid = amiibo.uuid,
+ .uuid_length = static_cast<u8>(tag_info.uuid.size()),
+ .padding_1 = {},
+ .protocol = 1, // TODO(ogniK): Figure out actual values
+ .tag_type = 2,
+ .padding_2 = {},
+ };
ctx.WriteBuffer(tag_info);
rb.Push(RESULT_SUCCESS);
}
@@ -236,7 +239,7 @@ private:
LOG_DEBUG(Service_NFP, "called");
IPC::ResponseBuilder rb{ctx, 2};
- auto amiibo = nfp_interface.GetAmiiboBuffer();
+ const auto& amiibo = nfp_interface.GetAmiiboBuffer();
ctx.WriteBuffer(amiibo.model_info);
rb.Push(RESULT_SUCCESS);
}
diff --git a/src/core/hle/service/nifm/nifm.cpp b/src/core/hle/service/nifm/nifm.cpp
index 01ddcdbd6..db7ec6d0e 100644
--- a/src/core/hle/service/nifm/nifm.cpp
+++ b/src/core/hle/service/nifm/nifm.cpp
@@ -9,6 +9,7 @@
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nifm/nifm.h"
#include "core/hle/service/service.h"
+#include "core/network/network.h"
#include "core/settings.h"
namespace Service::NIFM {
@@ -61,7 +62,7 @@ public:
{18, nullptr, "SetRequirementByRevision"},
{19, nullptr, "GetRequirement"},
{20, nullptr, "GetRevision"},
- {21, nullptr, "GetAppletInfo"},
+ {21, &IRequest::GetAppletInfo, "GetAppletInfo"},
{22, nullptr, "GetAdditionalInfo"},
{23, nullptr, "SetKeptInSleep"},
{24, nullptr, "RegisterSocketDescriptor"},
@@ -124,6 +125,16 @@ private:
rb.Push(RESULT_SUCCESS);
}
+ void GetAppletInfo(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 8};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<u32>(0);
+ rb.Push<u32>(0);
+ rb.Push<u32>(0);
+ }
+
Kernel::EventPair event1, event2;
};
@@ -174,6 +185,16 @@ private:
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(RESULT_SUCCESS);
}
+ void GetCurrentIpAddress(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_NIFM, "(STUBBED) called");
+
+ const auto [ipv4, error] = Network::GetHostIPv4Address();
+ UNIMPLEMENTED_IF(error != Network::Errno::SUCCESS);
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushRaw(ipv4);
+ }
void CreateTemporaryNetworkProfile(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NIFM, "called");
@@ -235,7 +256,7 @@ IGeneralService::IGeneralService(Core::System& system)
{9, nullptr, "SetNetworkProfile"},
{10, &IGeneralService::RemoveNetworkProfile, "RemoveNetworkProfile"},
{11, nullptr, "GetScanDataOld"},
- {12, nullptr, "GetCurrentIpAddress"},
+ {12, &IGeneralService::GetCurrentIpAddress, "GetCurrentIpAddress"},
{13, nullptr, "GetCurrentAccessPointOld"},
{14, &IGeneralService::CreateTemporaryNetworkProfile, "CreateTemporaryNetworkProfile"},
{15, nullptr, "GetCurrentIpConfigInfo"},
diff --git a/src/core/hle/service/ns/ns.cpp b/src/core/hle/service/ns/ns.cpp
index 886450be2..2594e6839 100644
--- a/src/core/hle/service/ns/ns.cpp
+++ b/src/core/hle/service/ns/ns.cpp
@@ -3,8 +3,10 @@
// Refer to the license.txt file included.
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
+#include "core/file_sys/vfs.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/ns/errors.h"
@@ -28,8 +30,8 @@ IAccountProxyInterface::IAccountProxyInterface() : ServiceFramework{"IAccountPro
IAccountProxyInterface::~IAccountProxyInterface() = default;
-IApplicationManagerInterface::IApplicationManagerInterface()
- : ServiceFramework{"IApplicationManagerInterface"} {
+IApplicationManagerInterface::IApplicationManagerInterface(Core::System& system_)
+ : ServiceFramework{"IApplicationManagerInterface"}, system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{0, nullptr, "ListApplicationRecord"},
@@ -297,7 +299,8 @@ void IApplicationManagerInterface::GetApplicationControlData(Kernel::HLERequestC
const auto size = ctx.GetWriteBufferSize();
- const FileSys::PatchManager pm{title_id};
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
std::vector<u8> out;
@@ -537,14 +540,14 @@ IFactoryResetInterface::IFactoryResetInterface::IFactoryResetInterface()
IFactoryResetInterface::~IFactoryResetInterface() = default;
-NS::NS(const char* name) : ServiceFramework{name} {
+NS::NS(const char* name, Core::System& system_) : ServiceFramework{name}, system{system_} {
// clang-format off
static const FunctionInfo functions[] = {
{7992, &NS::PushInterface<IECommerceInterface>, "GetECommerceInterface"},
{7993, &NS::PushInterface<IApplicationVersionInterface>, "GetApplicationVersionInterface"},
{7994, &NS::PushInterface<IFactoryResetInterface>, "GetFactoryResetInterface"},
{7995, &NS::PushInterface<IAccountProxyInterface>, "GetAccountProxyInterface"},
- {7996, &NS::PushInterface<IApplicationManagerInterface>, "GetApplicationManagerInterface"},
+ {7996, &NS::PushIApplicationManagerInterface, "GetApplicationManagerInterface"},
{7997, &NS::PushInterface<IDownloadTaskInterface>, "GetDownloadTaskInterface"},
{7998, &NS::PushInterface<IContentManagementInterface>, "GetContentManagementInterface"},
{7999, &NS::PushInterface<IDocumentInterface>, "GetDocumentInterface"},
@@ -557,7 +560,7 @@ NS::NS(const char* name) : ServiceFramework{name} {
NS::~NS() = default;
std::shared_ptr<IApplicationManagerInterface> NS::GetApplicationManagerInterface() const {
- return GetInterface<IApplicationManagerInterface>();
+ return GetInterface<IApplicationManagerInterface>(system);
}
class NS_DEV final : public ServiceFramework<NS_DEV> {
@@ -677,11 +680,11 @@ public:
void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
- std::make_shared<NS>("ns:am2")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:ec")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:rid")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:rt")->InstallAsService(service_manager);
- std::make_shared<NS>("ns:web")->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:am2", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:ec", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:rid", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:rt", system)->InstallAsService(service_manager);
+ std::make_shared<NS>("ns:web", system)->InstallAsService(service_manager);
std::make_shared<NS_DEV>()->InstallAsService(service_manager);
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 @@
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Service {
namespace FileSystem {
@@ -22,7 +26,7 @@ public:
class IApplicationManagerInterface final : public ServiceFramework<IApplicationManagerInterface> {
public:
- explicit IApplicationManagerInterface();
+ explicit IApplicationManagerInterface(Core::System& system_);
~IApplicationManagerInterface() override;
ResultVal<u8> GetApplicationDesiredLanguage(u32 supported_languages);
@@ -32,6 +36,8 @@ private:
void GetApplicationControlData(Kernel::HLERequestContext& ctx);
void GetApplicationDesiredLanguage(Kernel::HLERequestContext& ctx);
void ConvertApplicationLanguageToLanguageCode(Kernel::HLERequestContext& ctx);
+
+ Core::System& system;
};
class IApplicationVersionInterface final : public ServiceFramework<IApplicationVersionInterface> {
@@ -72,13 +78,13 @@ public:
class NS final : public ServiceFramework<NS> {
public:
- explicit NS(const char* name);
+ explicit NS(const char* name, Core::System& system_);
~NS() override;
std::shared_ptr<IApplicationManagerInterface> GetApplicationManagerInterface() const;
private:
- template <typename T>
+ template <typename T, typename... Args>
void PushInterface(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NS, "called");
@@ -87,13 +93,23 @@ private:
rb.PushIpcInterface<T>();
}
- template <typename T>
- std::shared_ptr<T> GetInterface() const {
+ void PushIApplicationManagerInterface(Kernel::HLERequestContext& ctx) {
+ LOG_DEBUG(Service_NS, "called");
+
+ IPC::ResponseBuilder rb{ctx, 2, 0, 1};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushIpcInterface<IApplicationManagerInterface>(system);
+ }
+
+ template <typename T, typename... Args>
+ std::shared_ptr<T> GetInterface(Args&&... args) const {
static_assert(std::is_base_of_v<Kernel::SessionRequestHandler, T>,
"Not a base of ServiceFrameworkBase");
- return std::make_shared<T>();
+ return std::make_shared<T>(std::forward<Args>(args)...);
}
+
+ Core::System& system;
};
/// Registers all NS services with the specified service manager.
diff --git a/src/core/hle/service/ns/pl_u.cpp b/src/core/hle/service/ns/pl_u.cpp
index 40838a225..5ccec2637 100644
--- a/src/core/hle/service/ns/pl_u.cpp
+++ b/src/core/hle/service/ns/pl_u.cpp
@@ -50,19 +50,9 @@ constexpr std::array<std::pair<FontArchives, const char*>, 7> SHARED_FONTS{
std::make_pair(FontArchives::Extension, "nintendo_ext2_003.bfttf"),
};
-constexpr std::array<const char*, 7> SHARED_FONTS_TTF{
- "FontStandard.ttf",
- "FontChineseSimplified.ttf",
- "FontExtendedChineseSimplified.ttf",
- "FontChineseTraditional.ttf",
- "FontKorean.ttf",
- "FontNintendoExtended.ttf",
- "FontNintendoExtended2.ttf",
-};
-
// The below data is specific to shared font data dumped from Switch on f/w 2.2
// Virtual address and offsets/sizes likely will vary by dump
-constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
+[[maybe_unused]] constexpr VAddr SHARED_FONT_MEM_VADDR{0x00000009d3016000ULL};
constexpr u32 EXPECTED_RESULT{0x7f9a0218}; // What we expect the decrypted bfttf first 4 bytes to be
constexpr u32 EXPECTED_MAGIC{0x36f81a1e}; // What we expect the encrypted bfttf first 4 bytes to be
constexpr u64 SHARED_FONT_MEM_SIZE{0x1100000};
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:
explicit nvdevice(Core::System& system) : system{system} {}
virtual ~nvdevice() = default;
- union Ioctl {
- u32_le raw;
- BitField<0, 8, u32> cmd;
- BitField<8, 8, u32> group;
- BitField<16, 14, u32> length;
- BitField<30, 1, u32> is_in;
- BitField<31, 1, u32> is_out;
- };
+ /**
+ * Handles an ioctl1 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param output A buffer where the output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) = 0;
+
+ /**
+ * Handles an ioctl2 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
+ * @param output A buffer where the output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
/**
- * Handles an ioctl request.
+ * Handles an ioctl3 request.
* @param command The ioctl command id.
* @param input A buffer containing the input data for the ioctl.
* @param output A buffer where the output data will be written to.
+ * @param inline_output A buffer where the inlined output data will be written to.
* @returns The result code of the ioctl.
*/
- virtual u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) = 0;
+ virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) = 0;
protected:
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
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvdisp_disp0 ::~nvdisp_disp0() = default;
-u32 nvdisp_disp0::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+NvResult nvdisp_disp0::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvdisp_disp0::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvdisp_disp0::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
void 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:
explicit nvdisp_disp0(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvdisp_disp0() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
/// Performs a screen flip, drawing the buffer pointed to by the handle.
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 d4ba88147..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,57 +17,77 @@
namespace Service::Nvidia::Devices {
-namespace NvErrCodes {
-constexpr u32 Success{};
-constexpr u32 OutOfMemory{static_cast<u32>(-12)};
-constexpr u32 InvalidInput{static_cast<u32>(-22)};
-} // namespace NvErrCodes
-
nvhost_as_gpu::nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
: nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
nvhost_as_gpu::~nvhost_as_gpu() = default;
-u32 nvhost_as_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocInitalizeExCommand:
- return InitalizeEx(input, output);
- case IoctlCommand::IocAllocateSpaceCommand:
- return AllocateSpace(input, output);
- case IoctlCommand::IocMapBufferExCommand:
- return MapBufferEx(input, output);
- case IoctlCommand::IocBindChannelCommand:
- return BindChannel(input, output);
- case IoctlCommand::IocGetVaRegionsCommand:
- return GetVARegions(input, output);
- case IoctlCommand::IocUnmapBufferCommand:
- return UnmapBuffer(input, output);
+NvResult nvhost_as_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 'A':
+ switch (command.cmd) {
+ case 0x1:
+ return BindChannel(input, output);
+ case 0x2:
+ return AllocateSpace(input, output);
+ case 0x3:
+ return FreeSpace(input, output);
+ case 0x5:
+ return UnmapBuffer(input, output);
+ case 0x6:
+ return MapBufferEx(input, output);
+ case 0x8:
+ return GetVARegions(input, output);
+ case 0x9:
+ return InitalizeEx(input, output);
+ case 0x14:
+ return Remap(input, output);
+ default:
+ break;
+ }
+ break;
default:
break;
}
- if (static_cast<IoctlCommand>(command.cmd.Value()) == IoctlCommand::IocRemapCommand) {
- return Remap(input, output);
- }
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- UNIMPLEMENTED_MSG("Unimplemented ioctl command");
- return 0;
+NvResult nvhost_as_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ switch (command.group) {
+ case 'A':
+ switch (command.cmd) {
+ case 0x8:
+ return GetVARegions(input, output, inline_output);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_as_gpu::InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlInitalizeEx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, big_page_size=0x{:X}", params.big_page_size);
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocSpace params{};
std::memcpy(&params, input.data(), input.size());
@@ -81,22 +101,36 @@ u32 nvhost_as_gpu::AllocateSpace(const std::vector<u8>& input, std::vector<u8>&
params.offset = system.GPU().MemoryManager().Allocate(size, params.align);
}
- auto result{NvErrCodes::Success};
+ auto result = NvResult::Success;
if (!params.offset) {
LOG_CRITICAL(Service_NVDRV, "allocation failed for size {}", size);
- result = NvErrCodes::OutOfMemory;
+ result = NvResult::InsufficientMemory;
}
std::memcpy(output.data(), &params, output.size());
return result;
}
-u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::FreeSpace(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlFreeSpace params{};
+ std::memcpy(&params, input.data(), input.size());
+
+ LOG_DEBUG(Service_NVDRV, "called, offset={:X}, pages={:X}, page_size={:X}", params.offset,
+ params.pages, params.page_size);
+
+ system.GPU().MemoryManager().Unmap(params.offset,
+ static_cast<std::size_t>(params.pages) * params.page_size);
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
+}
+
+NvResult nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output) {
const auto num_entries = input.size() / sizeof(IoctlRemapEntry);
LOG_DEBUG(Service_NVDRV, "called, num_entries=0x{:X}", num_entries);
- auto result{NvErrCodes::Success};
+ auto result = NvResult::Success;
std::vector<IoctlRemapEntry> entries(num_entries);
std::memcpy(entries.data(), input.data(), input.size());
@@ -107,7 +141,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
const auto object{nvmap_dev->GetObject(entry.nvmap_handle)};
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", entry.nvmap_handle);
- result = NvErrCodes::InvalidInput;
+ result = NvResult::InvalidState;
break;
}
@@ -118,7 +152,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
if (!addr) {
LOG_CRITICAL(Service_NVDRV, "map returned an invalid address!");
- result = NvErrCodes::InvalidInput;
+ result = NvResult::InvalidState;
break;
}
}
@@ -127,7 +161,7 @@ u32 nvhost_as_gpu::Remap(const std::vector<u8>& input, std::vector<u8>& output)
return result;
}
-u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlMapBufferEx params{};
std::memcpy(&params, input.data(), input.size());
@@ -141,7 +175,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
if (!object) {
LOG_CRITICAL(Service_NVDRV, "invalid nvmap_handle={:X}", params.nvmap_handle);
std::memcpy(output.data(), &params, output.size());
- return NvErrCodes::InvalidInput;
+ return NvResult::InvalidState;
}
// The real nvservices doesn't make a distinction between handles and ids, and
@@ -168,16 +202,16 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
params.mapping_size, params.offset);
std::memcpy(output.data(), &params, output.size());
- return NvErrCodes::InvalidInput;
+ return NvResult::InvalidState;
}
std::memcpy(output.data(), &params, output.size());
- return NvErrCodes::Success;
+ return NvResult::Success;
} else {
LOG_CRITICAL(Service_NVDRV, "address not mapped offset={}", params.offset);
std::memcpy(output.data(), &params, output.size());
- return NvErrCodes::InvalidInput;
+ return NvResult::InvalidState;
}
}
@@ -197,10 +231,10 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
params.offset = gpu.MemoryManager().Map(physical_address, params.offset, size);
}
- auto result{NvErrCodes::Success};
+ auto result = NvResult::Success;
if (!params.offset) {
LOG_CRITICAL(Service_NVDRV, "failed to map size={}", size);
- result = NvErrCodes::InvalidInput;
+ result = NvResult::InvalidState;
} else {
AddBufferMap(params.offset, size, physical_address, is_alloc);
}
@@ -209,7 +243,7 @@ u32 nvhost_as_gpu::MapBufferEx(const std::vector<u8>& input, std::vector<u8>& ou
return result;
}
-u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlUnmapBuffer params{};
std::memcpy(&params, input.data(), input.size());
@@ -222,20 +256,42 @@ u32 nvhost_as_gpu::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& ou
}
std::memcpy(output.data(), &params, output.size());
- return NvErrCodes::Success;
+ return NvResult::Success;
}
-u32 nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::BindChannel(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlBindChannel params{};
std::memcpy(&params, input.data(), input.size());
-
- LOG_DEBUG(Service_NVDRV, "called, fd={:X}", params.fd);
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}", params.fd);
channel = params.fd;
- return 0;
+ return NvResult::Success;
+}
+
+NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlGetVaRegions params{};
+ std::memcpy(&params, input.data(), input.size());
+
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called, buf_addr={:X}, buf_size={:X}", params.buf_addr,
+ params.buf_size);
+
+ params.buf_size = 0x30;
+ params.regions[0].offset = 0x04000000;
+ params.regions[0].page_size = 0x1000;
+ params.regions[0].pages = 0x3fbfff;
+
+ params.regions[1].offset = 0x04000000;
+ params.regions[1].page_size = 0x10000;
+ params.regions[1].pages = 0x1bffff;
+
+ // TODO(ogniK): This probably can stay stubbed but should add support way way later
+
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
}
-u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
IoctlGetVaRegions params{};
std::memcpy(&params, input.data(), input.size());
@@ -254,7 +310,8 @@ u32 nvhost_as_gpu::GetVARegions(const std::vector<u8>& input, std::vector<u8>& o
// TODO(ogniK): This probably can stay stubbed but should add support way way later
std::memcpy(output.data(), &params, output.size());
- return 0;
+ std::memcpy(inline_output.data(), &params.regions, inline_output.size());
+ return NvResult::Success;
}
std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gpu_addr) const {
@@ -265,7 +322,7 @@ std::optional<nvhost_as_gpu::BufferMap> nvhost_as_gpu::FindBufferMap(GPUVAddr gp
}
}
- return {};
+ return std::nullopt;
}
void nvhost_as_gpu::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
@@ -286,7 +343,7 @@ std::optional<std::size_t> nvhost_as_gpu::RemoveBufferMap(GPUVAddr gpu_addr) {
return size;
}
- return {};
+ return std::nullopt;
}
} // namespace Service::Nvidia::Devices
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 9a0cdff0c..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:
explicit nvhost_as_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_as_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
class BufferMap final {
@@ -74,31 +76,21 @@ private:
bool is_allocated{};
};
- enum class IoctlCommand : u32_le {
- IocInitalizeExCommand = 0x40284109,
- IocAllocateSpaceCommand = 0xC0184102,
- IocRemapCommand = 0x00000014,
- IocMapBufferExCommand = 0xC0284106,
- IocBindChannelCommand = 0x40044101,
- IocGetVaRegionsCommand = 0xC0404108,
- IocUnmapBufferCommand = 0xC0084105,
- };
-
struct IoctlInitalizeEx {
- u32_le big_page_size; // depends on GPU's available_big_page_sizes; 0=default
- s32_le as_fd; // ignored; passes 0
- u32_le flags; // passes 0
- u32_le reserved; // ignored; passes 0
- u64_le unk0;
- u64_le unk1;
- u64_le unk2;
+ u32_le big_page_size{}; // depends on GPU's available_big_page_sizes; 0=default
+ s32_le as_fd{}; // ignored; passes 0
+ u32_le flags{}; // passes 0
+ u32_le reserved{}; // ignored; passes 0
+ u64_le unk0{};
+ u64_le unk1{};
+ u64_le unk2{};
};
static_assert(sizeof(IoctlInitalizeEx) == 40, "IoctlInitalizeEx is incorrect size");
struct IoctlAllocSpace {
- u32_le pages;
- u32_le page_size;
- AddressSpaceFlags flags;
+ u32_le pages{};
+ u32_le page_size{};
+ AddressSpaceFlags flags{};
INSERT_PADDING_WORDS(1);
union {
u64_le offset;
@@ -107,63 +99,74 @@ private:
};
static_assert(sizeof(IoctlAllocSpace) == 24, "IoctlInitalizeEx is incorrect size");
+ struct IoctlFreeSpace {
+ u64_le offset{};
+ u32_le pages{};
+ u32_le page_size{};
+ };
+ static_assert(sizeof(IoctlFreeSpace) == 16, "IoctlFreeSpace is incorrect size");
+
struct IoctlRemapEntry {
- u16_le flags;
- u16_le kind;
- u32_le nvmap_handle;
- u32_le map_offset;
- u32_le offset;
- u32_le pages;
+ u16_le flags{};
+ u16_le kind{};
+ u32_le nvmap_handle{};
+ u32_le map_offset{};
+ u32_le offset{};
+ u32_le pages{};
};
static_assert(sizeof(IoctlRemapEntry) == 20, "IoctlRemapEntry is incorrect size");
struct IoctlMapBufferEx {
- AddressSpaceFlags flags; // bit0: fixed_offset, bit2: cacheable
- u32_le kind; // -1 is default
- u32_le nvmap_handle;
- u32_le page_size; // 0 means don't care
- s64_le buffer_offset;
- u64_le mapping_size;
- s64_le offset;
+ AddressSpaceFlags flags{}; // bit0: fixed_offset, bit2: cacheable
+ u32_le kind{}; // -1 is default
+ u32_le nvmap_handle{};
+ u32_le page_size{}; // 0 means don't care
+ s64_le buffer_offset{};
+ u64_le mapping_size{};
+ s64_le offset{};
};
static_assert(sizeof(IoctlMapBufferEx) == 40, "IoctlMapBufferEx is incorrect size");
struct IoctlUnmapBuffer {
- s64_le offset;
+ s64_le offset{};
};
static_assert(sizeof(IoctlUnmapBuffer) == 8, "IoctlUnmapBuffer is incorrect size");
struct IoctlBindChannel {
- u32_le fd;
+ s32_le fd{};
};
static_assert(sizeof(IoctlBindChannel) == 4, "IoctlBindChannel is incorrect size");
struct IoctlVaRegion {
- u64_le offset;
- u32_le page_size;
+ u64_le offset{};
+ u32_le page_size{};
INSERT_PADDING_WORDS(1);
- u64_le pages;
+ u64_le pages{};
};
static_assert(sizeof(IoctlVaRegion) == 24, "IoctlVaRegion is incorrect size");
struct IoctlGetVaRegions {
- u64_le buf_addr; // (contained output user ptr on linux, ignored)
- u32_le buf_size; // forced to 2*sizeof(struct va_region)
- u32_le reserved;
- IoctlVaRegion regions[2];
+ u64_le buf_addr{}; // (contained output user ptr on linux, ignored)
+ u32_le buf_size{}; // forced to 2*sizeof(struct va_region)
+ u32_le reserved{};
+ IoctlVaRegion regions[2]{};
};
static_assert(sizeof(IoctlGetVaRegions) == 16 + sizeof(IoctlVaRegion) * 2,
"IoctlGetVaRegions is incorrect size");
- u32 channel{};
+ s32 channel{};
+
+ NvResult InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult Remap(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult FreeSpace(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
- u32 InitalizeEx(const std::vector<u8>& input, std::vector<u8>& output);
- u32 AllocateSpace(const std::vector<u8>& input, std::vector<u8>& output);
- u32 Remap(const std::vector<u8>& input, std::vector<u8>& output);
- u32 MapBufferEx(const std::vector<u8>& input, std::vector<u8>& output);
- u32 UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
- u32 BindChannel(const std::vector<u8>& input, std::vector<u8>& output);
- u32 GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetVARegions(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output);
std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
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 b27ee0502..d90cf90a8 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.cpp
@@ -15,45 +15,59 @@
namespace Service::Nvidia::Devices {
-nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface)
- : nvdevice(system), events_interface{events_interface} {}
+nvhost_ctrl::nvhost_ctrl(Core::System& system, EventInterface& events_interface,
+ SyncpointManager& syncpoint_manager)
+ : nvdevice(system), events_interface{events_interface}, syncpoint_manager{syncpoint_manager} {}
nvhost_ctrl::~nvhost_ctrl() = default;
-u32 nvhost_ctrl::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocGetConfigCommand:
- return NvOsGetConfigU32(input, output);
- case IoctlCommand::IocCtrlEventWaitCommand:
- return IocCtrlEventWait(input, output, false, ctrl);
- case IoctlCommand::IocCtrlEventWaitAsyncCommand:
- return IocCtrlEventWait(input, output, true, ctrl);
- case IoctlCommand::IocCtrlEventRegisterCommand:
- return IocCtrlEventRegister(input, output);
- case IoctlCommand::IocCtrlEventUnregisterCommand:
- return IocCtrlEventUnregister(input, output);
- case IoctlCommand::IocCtrlEventSignalCommand:
- return IocCtrlEventSignal(input, output);
+NvResult nvhost_ctrl::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x1b:
+ return NvOsGetConfigU32(input, output);
+ case 0x1c:
+ return IocCtrlClearEventWait(input, output);
+ case 0x1d:
+ return IocCtrlEventWait(input, output, false);
+ case 0x1e:
+ return IocCtrlEventWait(input, output, true);
+ case 0x1f:
+ return IocCtrlEventRegister(input, output);
+ case 0x20:
+ return IocCtrlEventUnregister(input, output);
+ }
+ break;
default:
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ break;
}
+
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetConfigParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_TRACE(Service_NVDRV, "called, setting={}!{}", params.domain_str.data(),
params.param_str.data());
- return 0x30006; // Returns error on production mode
+ return NvResult::ConfigVarNotFound; // Returns error on production mode
}
-u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
- bool is_async, IoctlCtrl& ctrl) {
+NvResult nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output,
+ bool is_async) {
IocCtrlEventWaitParams params{};
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "syncpt_id={}, threshold={}, timeout={}, is_async={}",
@@ -70,19 +84,33 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter;
}
+ if (syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
+ params.value = syncpoint_manager.GetSyncpointMin(params.syncpt_id);
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::Success;
+ }
+
+ if (const auto new_value = syncpoint_manager.RefreshSyncpoint(params.syncpt_id);
+ syncpoint_manager.IsSyncpointExpired(params.syncpt_id, params.threshold)) {
+ params.value = new_value;
+ std::memcpy(output.data(), &params, sizeof(params));
+ return NvResult::Success;
+ }
+
auto event = events_interface.events[event_id];
auto& gpu = system.GPU();
+
// This is mostly to take into account unimplemented features. As synced
// gpu is always synced.
if (!gpu.IsAsync()) {
- event.writable->Signal();
+ event.event.writable->Signal();
return NvResult::Success;
}
auto lock = gpu.LockSync();
- const u32 current_syncpoint_value = gpu.GetSyncpointValue(params.syncpt_id);
+ const u32 current_syncpoint_value = event.fence.value;
const s32 diff = current_syncpoint_value - params.threshold;
if (diff >= 0) {
- event.writable->Signal();
+ event.event.writable->Signal();
params.value = current_syncpoint_value;
std::memcpy(output.data(), &params, sizeof(params));
return NvResult::Success;
@@ -109,12 +137,9 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
params.value = ((params.syncpt_id & 0xfff) << 16) | 0x10000000;
}
params.value |= event_id;
- event.writable->Clear();
+ event.event.writable->Clear();
gpu.RegisterSyncptInterrupt(params.syncpt_id, target_value);
- if (!is_async && ctrl.fresh_call) {
- ctrl.must_delay = true;
- ctrl.timeout = params.timeout;
- ctrl.event_id = event_id;
+ if (!is_async) {
return NvResult::Timeout;
}
std::memcpy(output.data(), &params, sizeof(params));
@@ -124,7 +149,7 @@ u32 nvhost_ctrl::IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>&
return NvResult::BadParameter;
}
-u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventRegisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
@@ -139,7 +164,8 @@ u32 nvhost_ctrl::IocCtrlEventRegister(const std::vector<u8>& input, std::vector<
return NvResult::Success;
}
-u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input,
+ std::vector<u8>& output) {
IocCtrlEventUnregisterParams params{};
std::memcpy(&params, input.data(), sizeof(params));
const u32 event_id = params.user_event_id & 0x00FF;
@@ -154,24 +180,22 @@ u32 nvhost_ctrl::IocCtrlEventUnregister(const std::vector<u8>& input, std::vecto
return NvResult::Success;
}
-u32 nvhost_ctrl::IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl::IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output) {
IocCtrlEventSignalParams params{};
std::memcpy(&params, input.data(), sizeof(params));
- // TODO(Blinkhawk): This is normally called when an NvEvents timeout on WaitSynchronization
- // It is believed from RE to cancel the GPU Event. However, better research is required
- u32 event_id = params.user_event_id & 0x00FF;
- LOG_WARNING(Service_NVDRV, "(STUBBED) called, user_event_id: {:X}", event_id);
+
+ u32 event_id = params.event_id & 0x00FF;
+ LOG_WARNING(Service_NVDRV, "cleared event wait on, event_id: {:X}", event_id);
+
if (event_id >= MaxNvEvents) {
return NvResult::BadParameter;
}
if (events_interface.status[event_id] == EventState::Waiting) {
- auto& gpu = system.GPU();
- if (gpu.CancelSyncptInterrupt(events_interface.assigned_syncpt[event_id],
- events_interface.assigned_value[event_id])) {
- events_interface.LiberateEvent(event_id);
- events_interface.events[event_id].writable->Signal();
- }
+ events_interface.LiberateEvent(event_id);
}
+
+ syncpoint_manager.RefreshSyncpoint(events_interface.events[event_id].fence.id);
+
return NvResult::Success;
}
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
index 9898623de..c5aa1362a 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_ctrl.h
@@ -14,137 +14,120 @@ namespace Service::Nvidia::Devices {
class nvhost_ctrl final : public nvdevice {
public:
- explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface);
+ explicit nvhost_ctrl(Core::System& system, EventInterface& events_interface,
+ SyncpointManager& syncpoint_manager);
~nvhost_ctrl() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocSyncptReadCommand = 0xC0080014,
- IocSyncptIncrCommand = 0x40040015,
- IocSyncptWaitCommand = 0xC00C0016,
- IocModuleMutexCommand = 0x40080017,
- IocModuleRegRDWRCommand = 0xC0180018,
- IocSyncptWaitexCommand = 0xC0100019,
- IocSyncptReadMaxCommand = 0xC008001A,
- IocGetConfigCommand = 0xC183001B,
- IocCtrlEventSignalCommand = 0xC004001C,
- IocCtrlEventWaitCommand = 0xC010001D,
- IocCtrlEventWaitAsyncCommand = 0xC010001E,
- IocCtrlEventRegisterCommand = 0xC004001F,
- IocCtrlEventUnregisterCommand = 0xC0040020,
- IocCtrlEventKillCommand = 0x40080021,
- };
struct IocSyncptReadParams {
- u32_le id;
- u32_le value;
+ u32_le id{};
+ u32_le value{};
};
static_assert(sizeof(IocSyncptReadParams) == 8, "IocSyncptReadParams is incorrect size");
struct IocSyncptIncrParams {
- u32_le id;
+ u32_le id{};
};
static_assert(sizeof(IocSyncptIncrParams) == 4, "IocSyncptIncrParams is incorrect size");
struct IocSyncptWaitParams {
- u32_le id;
- u32_le thresh;
- s32_le timeout;
+ u32_le id{};
+ u32_le thresh{};
+ s32_le timeout{};
};
static_assert(sizeof(IocSyncptWaitParams) == 12, "IocSyncptWaitParams is incorrect size");
struct IocModuleMutexParams {
- u32_le id;
- u32_le lock; // (0 = unlock and 1 = lock)
+ u32_le id{};
+ u32_le lock{}; // (0 = unlock and 1 = lock)
};
static_assert(sizeof(IocModuleMutexParams) == 8, "IocModuleMutexParams is incorrect size");
struct IocModuleRegRDWRParams {
- u32_le id;
- u32_le num_offsets;
- u32_le block_size;
- u32_le offsets;
- u32_le values;
- u32_le write;
+ u32_le id{};
+ u32_le num_offsets{};
+ u32_le block_size{};
+ u32_le offsets{};
+ u32_le values{};
+ u32_le write{};
};
static_assert(sizeof(IocModuleRegRDWRParams) == 24, "IocModuleRegRDWRParams is incorrect size");
struct IocSyncptWaitexParams {
- u32_le id;
- u32_le thresh;
- s32_le timeout;
- u32_le value;
+ u32_le id{};
+ u32_le thresh{};
+ s32_le timeout{};
+ u32_le value{};
};
static_assert(sizeof(IocSyncptWaitexParams) == 16, "IocSyncptWaitexParams is incorrect size");
struct IocSyncptReadMaxParams {
- u32_le id;
- u32_le value;
+ u32_le id{};
+ u32_le value{};
};
static_assert(sizeof(IocSyncptReadMaxParams) == 8, "IocSyncptReadMaxParams is incorrect size");
struct IocGetConfigParams {
- std::array<char, 0x41> domain_str;
- std::array<char, 0x41> param_str;
- std::array<char, 0x101> config_str;
+ std::array<char, 0x41> domain_str{};
+ std::array<char, 0x41> param_str{};
+ std::array<char, 0x101> config_str{};
};
static_assert(sizeof(IocGetConfigParams) == 387, "IocGetConfigParams is incorrect size");
struct IocCtrlEventSignalParams {
- u32_le user_event_id;
+ u32_le event_id{};
};
static_assert(sizeof(IocCtrlEventSignalParams) == 4,
"IocCtrlEventSignalParams is incorrect size");
struct IocCtrlEventWaitParams {
- u32_le syncpt_id;
- u32_le threshold;
- s32_le timeout;
- u32_le value;
+ u32_le syncpt_id{};
+ u32_le threshold{};
+ s32_le timeout{};
+ u32_le value{};
};
static_assert(sizeof(IocCtrlEventWaitParams) == 16, "IocCtrlEventWaitParams is incorrect size");
struct IocCtrlEventWaitAsyncParams {
- u32_le syncpt_id;
- u32_le threshold;
- u32_le timeout;
- u32_le value;
+ u32_le syncpt_id{};
+ u32_le threshold{};
+ u32_le timeout{};
+ u32_le value{};
};
static_assert(sizeof(IocCtrlEventWaitAsyncParams) == 16,
"IocCtrlEventWaitAsyncParams is incorrect size");
struct IocCtrlEventRegisterParams {
- u32_le user_event_id;
+ u32_le user_event_id{};
};
static_assert(sizeof(IocCtrlEventRegisterParams) == 4,
"IocCtrlEventRegisterParams is incorrect size");
struct IocCtrlEventUnregisterParams {
- u32_le user_event_id;
+ u32_le user_event_id{};
};
static_assert(sizeof(IocCtrlEventUnregisterParams) == 4,
"IocCtrlEventUnregisterParams is incorrect size");
struct IocCtrlEventKill {
- u64_le user_events;
+ u64_le user_events{};
};
static_assert(sizeof(IocCtrlEventKill) == 8, "IocCtrlEventKill is incorrect size");
- u32 NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
-
- u32 IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async,
- IoctlCtrl& ctrl);
-
- u32 IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
-
- u32 IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
-
- u32 IocCtrlEventSignal(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult NvOsGetConfigU32(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlEventWait(const std::vector<u8>& input, std::vector<u8>& output, bool is_async);
+ NvResult IocCtrlEventRegister(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlEventUnregister(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCtrlClearEventWait(const std::vector<u8>& input, std::vector<u8>& output);
EventInterface& events_interface;
+ SyncpointManager& syncpoint_manager;
};
} // namespace Service::Nvidia::Devices
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 {
nvhost_ctrl_gpu::nvhost_ctrl_gpu(Core::System& system) : nvdevice(system) {}
nvhost_ctrl_gpu::~nvhost_ctrl_gpu() = default;
-u32 nvhost_ctrl_gpu::ioctl(Ioctl command, const std::vector<u8>& input,
- const std::vector<u8>& input2, std::vector<u8>& output,
- std::vector<u8>& output2, IoctlCtrl& ctrl, IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocGetCharacteristicsCommand:
- return GetCharacteristics(input, output, output2, version);
- case IoctlCommand::IocGetTPCMasksCommand:
- return GetTPCMasks(input, output, output2, version);
- case IoctlCommand::IocGetActiveSlotMaskCommand:
- return GetActiveSlotMask(input, output);
- case IoctlCommand::IocZcullGetCtxSizeCommand:
- return ZCullGetCtxSize(input, output);
- case IoctlCommand::IocZcullGetInfo:
- return ZCullGetInfo(input, output);
- case IoctlCommand::IocZbcSetTable:
- return ZBCSetTable(input, output);
- case IoctlCommand::IocZbcQueryTable:
- return ZBCQueryTable(input, output);
- case IoctlCommand::IocFlushL2:
- return FlushL2(input, output);
- case IoctlCommand::IocGetGpuTime:
- return GetGpuTime(input, output);
+NvResult nvhost_ctrl_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 'G':
+ switch (command.cmd) {
+ case 0x1:
+ return ZCullGetCtxSize(input, output);
+ case 0x2:
+ return ZCullGetInfo(input, output);
+ case 0x3:
+ return ZBCSetTable(input, output);
+ case 0x4:
+ return ZBCQueryTable(input, output);
+ case 0x5:
+ return GetCharacteristics(input, output);
+ case 0x6:
+ return GetTPCMasks(input, output);
+ case 0x7:
+ return FlushL2(input, output);
+ case 0x14:
+ return GetActiveSlotMask(input, output);
+ case 0x1c:
+ return GetGpuTime(input, output);
+ default:
+ break;
+ }
+ break;
+ }
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_ctrl_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output, std::vector<u8>& inline_output) {
+ switch (command.group) {
+ case 'G':
+ switch (command.cmd) {
+ case 0x5:
+ return GetCharacteristics(input, output, inline_output);
+ case 0x6:
+ return GetTPCMasks(input, output, inline_output);
+ default:
+ break;
+ }
+ break;
default:
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ break;
}
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
- std::vector<u8>& output2, IoctlVersion version) {
+NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input,
+ std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlCharacteristics params{};
std::memcpy(&params, input.data(), input.size());
@@ -88,36 +115,83 @@ u32 nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vecto
params.gc.gr_compbit_store_base_hw = 0x0;
params.gpu_characteristics_buf_size = 0xA0;
params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
+}
- if (version == IoctlVersion::Version3) {
- std::memcpy(output.data(), input.data(), output.size());
- std::memcpy(output2.data(), &params.gc, output2.size());
- } else {
- std::memcpy(output.data(), &params, output.size());
- }
- return 0;
+NvResult nvhost_ctrl_gpu::GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ LOG_DEBUG(Service_NVDRV, "called");
+ IoctlCharacteristics params{};
+ std::memcpy(&params, input.data(), input.size());
+ params.gc.arch = 0x120;
+ params.gc.impl = 0xb;
+ params.gc.rev = 0xa1;
+ params.gc.num_gpc = 0x1;
+ params.gc.l2_cache_size = 0x40000;
+ params.gc.on_board_video_memory_size = 0x0;
+ params.gc.num_tpc_per_gpc = 0x2;
+ params.gc.bus_type = 0x20;
+ params.gc.big_page_size = 0x20000;
+ params.gc.compression_page_size = 0x20000;
+ params.gc.pde_coverage_bit_count = 0x1B;
+ params.gc.available_big_page_sizes = 0x30000;
+ params.gc.gpc_mask = 0x1;
+ params.gc.sm_arch_sm_version = 0x503;
+ params.gc.sm_arch_spa_version = 0x503;
+ params.gc.sm_arch_warp_count = 0x80;
+ params.gc.gpu_va_bit_count = 0x28;
+ params.gc.reserved = 0x0;
+ params.gc.flags = 0x55;
+ params.gc.twod_class = 0x902D;
+ params.gc.threed_class = 0xB197;
+ params.gc.compute_class = 0xB1C0;
+ params.gc.gpfifo_class = 0xB06F;
+ params.gc.inline_to_memory_class = 0xA140;
+ params.gc.dma_copy_class = 0xB0B5;
+ params.gc.max_fbps_count = 0x1;
+ params.gc.fbp_en_mask = 0x0;
+ params.gc.max_ltc_per_fbp = 0x2;
+ params.gc.max_lts_per_ltc = 0x1;
+ params.gc.max_tex_per_tpc = 0x0;
+ params.gc.max_gpc_count = 0x1;
+ params.gc.rop_l2_en_mask_0 = 0x21D70;
+ params.gc.rop_l2_en_mask_1 = 0x0;
+ params.gc.chipname = 0x6230326D67;
+ params.gc.gr_compbit_store_base_hw = 0x0;
+ params.gpu_characteristics_buf_size = 0xA0;
+ params.gpu_characteristics_buf_addr = 0xdeadbeef; // Cannot be 0 (UNUSED)
+
+ std::memcpy(output.data(), input.data(), output.size());
+ std::memcpy(inline_output.data(), &params.gc, inline_output.size());
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
- std::vector<u8>& output2, IoctlVersion version) {
+NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGpuGetTpcMasksArgs params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
if (params.mask_buffer_size != 0) {
params.tcp_mask = 3;
}
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::Success;
+}
- if (version == IoctlVersion::Version3) {
- std::memcpy(output.data(), input.data(), output.size());
- std::memcpy(output2.data(), &params.tcp_mask, output2.size());
- } else {
- std::memcpy(output.data(), &params, output.size());
+NvResult nvhost_ctrl_gpu::GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ IoctlGpuGetTpcMasksArgs params{};
+ std::memcpy(&params, input.data(), input.size());
+ LOG_DEBUG(Service_NVDRV, "called, mask_buffer_size=0x{:X}", params.mask_buffer_size);
+ if (params.mask_buffer_size != 0) {
+ params.tcp_mask = 3;
}
-
- return 0;
+ std::memcpy(output.data(), &params, output.size());
+ std::memcpy(inline_output.data(), &params.tcp_mask, inline_output.size());
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlActiveSlotMask params{};
@@ -127,10 +201,10 @@ u32 nvhost_ctrl_gpu::GetActiveSlotMask(const std::vector<u8>& input, std::vector
params.slot = 0x07;
params.mask = 0x01;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlZcullGetCtxSize params{};
@@ -139,10 +213,10 @@ u32 nvhost_ctrl_gpu::ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u
}
params.size = 0x1;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlNvgpuGpuZcullGetInfoArgs params{};
@@ -162,47 +236,47 @@ u32 nvhost_ctrl_gpu::ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>&
params.subregion_height_align_pixels = 0x40;
params.subregion_count = 0x10;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcSetTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO(ogniK): What does this even actually do?
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlZbcQueryTable params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::FlushL2(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
IoctlFlushL2 params{};
std::memcpy(&params, input.data(), input.size());
// TODO : To implement properly
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_ctrl_gpu::GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlGetGpuTime params{};
std::memcpy(&params, input.data(), input.size());
params.gpu_time = static_cast<u64_le>(system.CoreTiming().GetGlobalTimeNs().count());
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
} // 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:
explicit nvhost_ctrl_gpu(Core::System& system);
~nvhost_ctrl_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocGetCharacteristicsCommand = 0xC0B04705,
- IocGetTPCMasksCommand = 0xC0184706,
- IocGetActiveSlotMaskCommand = 0x80084714,
- IocZcullGetCtxSizeCommand = 0x80044701,
- IocZcullGetInfo = 0x80284702,
- IocZbcSetTable = 0x402C4703,
- IocZbcQueryTable = 0xC0344704,
- IocFlushL2 = 0x40084707,
- IocInvalICache = 0x4008470D,
- IocSetMmudebugMode = 0x4008470E,
- IocSetSmDebugMode = 0x4010470F,
- IocWaitForPause = 0xC0084710,
- IocGetTcpExceptionEnStatus = 0x80084711,
- IocNumVsms = 0x80084712,
- IocVsmsMapping = 0xC0044713,
- IocGetErrorChannelUserData = 0xC008471B,
- IocGetGpuTime = 0xC010471C,
- IocGetCpuTimeCorrelationInfo = 0xC108471D,
- };
-
struct IoctlGpuCharacteristics {
u32_le arch; // 0x120 (NVGPU_GPU_ARCH_GM200)
u32_le impl; // 0xB (NVGPU_GPU_IMPL_GM20B)
@@ -159,17 +140,21 @@ private:
};
static_assert(sizeof(IoctlGetGpuTime) == 0x10, "IoctlGetGpuTime is incorrect size");
- u32 GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
- std::vector<u8>& output2, IoctlVersion version);
- u32 GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output, std::vector<u8>& output2,
- IoctlVersion version);
- u32 GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
- u32 FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
- u32 GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetCharacteristics(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output);
+
+ NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetTPCMasks(const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output);
+
+ NvResult GetActiveSlotMask(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZCullGetCtxSize(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZCullGetInfo(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZBCSetTable(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZBCQueryTable(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult FlushL2(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetGpuTime(const std::vector<u8>& input, std::vector<u8>& output);
};
} // 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 f1966ac0e..af8b3d9f1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.cpp
@@ -7,117 +7,148 @@
#include "common/logging/log.h"
#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_gpu.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/memory.h"
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
namespace Service::Nvidia::Devices {
-nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
- : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_gpu::nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)), syncpoint_manager{syncpoint_manager} {
+ channel_fence.id = syncpoint_manager.AllocateSyncpoint();
+ channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
+}
+
nvhost_gpu::~nvhost_gpu() = default;
-u32 nvhost_gpu::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
- case IoctlCommand::IocSetClientDataCommand:
- return SetClientData(input, output);
- case IoctlCommand::IocGetClientDataCommand:
- return GetClientData(input, output);
- case IoctlCommand::IocZCullBind:
- return ZCullBind(input, output);
- case IoctlCommand::IocSetErrorNotifierCommand:
- return SetErrorNotifier(input, output);
- case IoctlCommand::IocChannelSetPriorityCommand:
- return SetChannelPriority(input, output);
- case IoctlCommand::IocAllocGPFIFOEx2Command:
- return AllocGPFIFOEx2(input, output);
- case IoctlCommand::IocAllocObjCtxCommand:
- return AllocateObjectContext(input, output);
- case IoctlCommand::IocChannelGetWaitbaseCommand:
- return GetWaitbase(input, output);
- case IoctlCommand::IocChannelSetTimeoutCommand:
- return ChannelSetTimeout(input, output);
- case IoctlCommand::IocChannelSetTimeslice:
- return ChannelSetTimeslice(input, output);
- default:
+NvResult nvhost_gpu::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x3:
+ return GetWaitbase(input, output);
+ default:
+ break;
+ }
+ break;
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input, output);
+ case 0x3:
+ return ChannelSetTimeout(input, output);
+ case 0x8:
+ return SubmitGPFIFOBase(input, output, false);
+ case 0x9:
+ return AllocateObjectContext(input, output);
+ case 0xb:
+ return ZCullBind(input, output);
+ case 0xc:
+ return SetErrorNotifier(input, output);
+ case 0xd:
+ return SetChannelPriority(input, output);
+ case 0x1a:
+ return AllocGPFIFOEx2(input, output);
+ case 0x1b:
+ return SubmitGPFIFOBase(input, output, true);
+ case 0x1d:
+ return ChannelSetTimeslice(input, output);
+ default:
+ break;
+ }
+ break;
+ case 'G':
+ switch (command.cmd) {
+ case 0x14:
+ return SetClientData(input, output);
+ case 0x15:
+ return GetClientData(input, output);
+ default:
+ break;
+ }
break;
}
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+};
- if (command.group == NVGPU_IOCTL_MAGIC) {
- if (command.cmd == NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO) {
- return SubmitGPFIFO(input, output);
- }
- if (command.cmd == NVGPU_IOCTL_CHANNEL_KICKOFF_PB) {
- return KickoffPB(input, output, input2, version);
+NvResult nvhost_gpu::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 'H':
+ switch (command.cmd) {
+ case 0x1b:
+ return SubmitGPFIFOBase(input, inline_input, output);
}
+ break;
}
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
-};
+NvResult nvhost_gpu::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
-u32 nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
user_data = params.data;
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetClientData(const std::vector<u8>& input, std::vector<u8>& output) {
LOG_DEBUG(Service_NVDRV, "called");
IoctlClientData params{};
std::memcpy(&params, input.data(), input.size());
params.data = user_data;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ZCullBind(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&zcull_params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, gpu_va={:X}, mode={:X}", zcull_params.gpu_va,
zcull_params.mode);
std::memcpy(output.data(), &zcull_params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetErrorNotifier params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, offset={:X}, size={:X}, mem={:X}", params.offset,
params.size, params.mem);
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output) {
std::memcpy(&channel_priority, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "(STUBBED) called, priority={:X}", channel_priority);
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocGpfifoEx2 params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV,
@@ -126,15 +157,15 @@ u32 nvhost_gpu::AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& ou
params.num_entries, params.flags, params.unk0, params.unk1, params.unk2,
params.unk3);
- auto& gpu = system.GPU();
- params.fence_out.id = assigned_syncpoints;
- params.fence_out.value = gpu.GetSyncpointValue(assigned_syncpoints);
- assigned_syncpoints++;
+ channel_fence.value = system.GPU().GetSyncpointValue(channel_fence.id);
+
+ params.fence_out = channel_fence;
+
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlAllocObjCtx params{};
std::memcpy(&params, input.data(), input.size());
LOG_WARNING(Service_NVDRV, "(STUBBED) called, class_num={:X}, flags={:X}", params.class_num,
@@ -142,102 +173,149 @@ u32 nvhost_gpu::AllocateObjectContext(const std::vector<u8>& input, std::vector<
params.obj_id = 0x0;
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output) {
- if (input.size() < sizeof(IoctlSubmitGpfifo)) {
- UNIMPLEMENTED();
+static std::vector<Tegra::CommandHeader> BuildWaitCommandList(Fence fence) {
+ return {
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
+ Tegra::SubmissionMode::Increasing),
+ {fence.value},
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
+ Tegra::SubmissionMode::Increasing),
+ Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Acquire, fence.id),
+ };
+}
+
+static std::vector<Tegra::CommandHeader> BuildIncrementCommandList(Fence fence, u32 add_increment) {
+ std::vector<Tegra::CommandHeader> result{
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceValue, 1,
+ Tegra::SubmissionMode::Increasing),
+ {}};
+
+ for (u32 count = 0; count < add_increment; ++count) {
+ result.emplace_back(Tegra::BuildCommandHeader(Tegra::BufferMethods::FenceAction, 1,
+ Tegra::SubmissionMode::Increasing));
+ result.emplace_back(
+ Tegra::GPU::FenceAction::Build(Tegra::GPU::FenceOperation::Increment, fence.id));
}
- IoctlSubmitGpfifo params{};
- std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
+
+ return result;
+}
+
+static std::vector<Tegra::CommandHeader> BuildIncrementWithWfiCommandList(Fence fence,
+ u32 add_increment) {
+ std::vector<Tegra::CommandHeader> result{
+ Tegra::BuildCommandHeader(Tegra::BufferMethods::WaitForInterrupt, 1,
+ Tegra::SubmissionMode::Increasing),
+ {}};
+ const std::vector<Tegra::CommandHeader> increment{
+ BuildIncrementCommandList(fence, add_increment)};
+
+ result.insert(result.end(), increment.begin(), increment.end());
+
+ return result;
+}
+
+NvResult nvhost_gpu::SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
+ Tegra::CommandList&& entries) {
LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
params.num_entries, params.flags.raw);
- ASSERT_MSG(input.size() == sizeof(IoctlSubmitGpfifo) +
- params.num_entries * sizeof(Tegra::CommandListHeader),
- "Incorrect input size");
+ auto& gpu = system.GPU();
- Tegra::CommandList entries(params.num_entries);
- std::memcpy(entries.data(), &input[sizeof(IoctlSubmitGpfifo)],
- params.num_entries * sizeof(Tegra::CommandListHeader));
+ params.fence_out.id = channel_fence.id;
- UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
- UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
+ if (params.flags.add_wait.Value() &&
+ !syncpoint_manager.IsSyncpointExpired(params.fence_out.id, params.fence_out.value)) {
+ gpu.PushGPUEntries(Tegra::CommandList{BuildWaitCommandList(params.fence_out)});
+ }
- auto& gpu = system.GPU();
- u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
- if (params.flags.increment.Value()) {
- params.fence_out.value += current_syncpoint_value;
+ if (params.flags.add_increment.Value() || params.flags.increment.Value()) {
+ const u32 increment_value = params.flags.increment.Value() ? params.fence_out.value : 0;
+ params.fence_out.value = syncpoint_manager.IncreaseSyncpoint(
+ params.fence_out.id, params.AddIncrementValue() + increment_value);
} else {
- params.fence_out.value = current_syncpoint_value;
+ params.fence_out.value = syncpoint_manager.GetSyncpointMax(params.fence_out.id);
}
+
gpu.PushGPUEntries(std::move(entries));
+ if (params.flags.add_increment.Value()) {
+ if (params.flags.suppress_wfi) {
+ gpu.PushGPUEntries(Tegra::CommandList{
+ BuildIncrementCommandList(params.fence_out, params.AddIncrementValue())});
+ } else {
+ gpu.PushGPUEntries(Tegra::CommandList{
+ BuildIncrementWithWfiCommandList(params.fence_out, params.AddIncrementValue())});
+ }
+ }
+
std::memcpy(output.data(), &params, sizeof(IoctlSubmitGpfifo));
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
- const std::vector<u8>& input2, IoctlVersion version) {
+NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+ bool kickoff) {
if (input.size() < sizeof(IoctlSubmitGpfifo)) {
UNIMPLEMENTED();
+ return NvResult::InvalidSize;
}
IoctlSubmitGpfifo params{};
std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
- LOG_TRACE(Service_NVDRV, "called, gpfifo={:X}, num_entries={:X}, flags={:X}", params.address,
- params.num_entries, params.flags.raw);
-
Tegra::CommandList entries(params.num_entries);
- if (version == IoctlVersion::Version2) {
- std::memcpy(entries.data(), input2.data(),
- params.num_entries * sizeof(Tegra::CommandListHeader));
- } else {
- system.Memory().ReadBlock(params.address, entries.data(),
- params.num_entries * sizeof(Tegra::CommandListHeader));
- }
- UNIMPLEMENTED_IF(params.flags.add_wait.Value() != 0);
- UNIMPLEMENTED_IF(params.flags.add_increment.Value() != 0);
- auto& gpu = system.GPU();
- u32 current_syncpoint_value = gpu.GetSyncpointValue(params.fence_out.id);
- if (params.flags.increment.Value()) {
- params.fence_out.value += current_syncpoint_value;
+ if (kickoff) {
+ system.Memory().ReadBlock(params.address, entries.command_lists.data(),
+ params.num_entries * sizeof(Tegra::CommandListHeader));
} else {
- params.fence_out.value = current_syncpoint_value;
+ std::memcpy(entries.command_lists.data(), &input[sizeof(IoctlSubmitGpfifo)],
+ params.num_entries * sizeof(Tegra::CommandListHeader));
}
- gpu.PushGPUEntries(std::move(entries));
- std::memcpy(output.data(), &params, output.size());
- return 0;
+ return SubmitGPFIFOImpl(params, output, std::move(entries));
+}
+
+NvResult nvhost_gpu::SubmitGPFIFOBase(const std::vector<u8>& input,
+ const std::vector<u8>& input_inline,
+ std::vector<u8>& output) {
+ if (input.size() < sizeof(IoctlSubmitGpfifo)) {
+ UNIMPLEMENTED();
+ return NvResult::InvalidSize;
+ }
+ IoctlSubmitGpfifo params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlSubmitGpfifo));
+ Tegra::CommandList entries(params.num_entries);
+ std::memcpy(entries.command_lists.data(), input_inline.data(), input_inline.size());
+ return SubmitGPFIFOImpl(params, output, std::move(entries));
}
-u32 nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlGetWaitbase params{};
std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
LOG_INFO(Service_NVDRV, "called, unknown=0x{:X}", params.unknown);
params.value = 0; // Seems to be hard coded at 0
std::memcpy(output.data(), &params, output.size());
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlChannelSetTimeout params{};
std::memcpy(&params, input.data(), sizeof(IoctlChannelSetTimeout));
LOG_INFO(Service_NVDRV, "called, timeout=0x{:X}", params.timeout);
- return 0;
+ return NvResult::Success;
}
-u32 nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_gpu::ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetTimeslice params{};
std::memcpy(&params, input.data(), sizeof(IoctlSetTimeslice));
LOG_INFO(Service_NVDRV, "called, timeslice=0x{:X}", params.timeslice);
channel_timeslice = params.timeslice;
- return 0;
+ return NvResult::Success;
}
} // 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 2ac74743f..e0298b4fe 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_gpu.h
@@ -11,46 +11,28 @@
#include "common/swap.h"
#include "core/hle/service/nvdrv/devices/nvdevice.h"
#include "core/hle/service/nvdrv/nvdata.h"
+#include "video_core/dma_pusher.h"
+
+namespace Service::Nvidia {
+class SyncpointManager;
+}
namespace Service::Nvidia::Devices {
class nvmap;
-constexpr u32 NVGPU_IOCTL_MAGIC('H');
-constexpr u32 NVGPU_IOCTL_CHANNEL_SUBMIT_GPFIFO(0x8);
-constexpr u32 NVGPU_IOCTL_CHANNEL_KICKOFF_PB(0x1b);
-
class nvhost_gpu final : public nvdevice {
public:
- explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ explicit nvhost_gpu(Core::System& system, std::shared_ptr<nvmap> nvmap_dev,
+ SyncpointManager& syncpoint_manager);
~nvhost_gpu() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- IocAllocGPFIFOCommand = 0x40084805,
- IocSetClientDataCommand = 0x40084714,
- IocGetClientDataCommand = 0x80084715,
- IocZCullBind = 0xc010480b,
- IocSetErrorNotifierCommand = 0xC018480C,
- IocChannelSetPriorityCommand = 0x4004480D,
- IocEnableCommand = 0x0000480E,
- IocDisableCommand = 0x0000480F,
- IocPreemptCommand = 0x00004810,
- IocForceResetCommand = 0x00004811,
- IocEventIdControlCommand = 0x40084812,
- IocGetErrorNotificationCommand = 0xC0104817,
- IocAllocGPFIFOExCommand = 0x40204818,
- IocAllocGPFIFOEx2Command = 0xC020481A,
- IocAllocObjCtxCommand = 0xC0104809,
- IocChannelGetWaitbaseCommand = 0xC0080003,
- IocChannelSetTimeoutCommand = 0x40044803,
- IocChannelSetTimeslice = 0xC004481D,
- };
-
enum class CtxObjects : u32_le {
Ctx2D = 0x902D,
Ctx3D = 0xB197,
@@ -61,63 +43,63 @@ private:
};
struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
+ s32_le nvmap_fd{};
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
struct IoctlChannelSetTimeout {
- u32_le timeout;
+ u32_le timeout{};
};
static_assert(sizeof(IoctlChannelSetTimeout) == 4, "IoctlChannelSetTimeout is incorrect size");
struct IoctlAllocGPFIFO {
- u32_le num_entries;
- u32_le flags;
+ u32_le num_entries{};
+ u32_le flags{};
};
static_assert(sizeof(IoctlAllocGPFIFO) == 8, "IoctlAllocGPFIFO is incorrect size");
struct IoctlClientData {
- u64_le data;
+ u64_le data{};
};
static_assert(sizeof(IoctlClientData) == 8, "IoctlClientData is incorrect size");
struct IoctlZCullBind {
- u64_le gpu_va;
- u32_le mode; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
+ u64_le gpu_va{};
+ u32_le mode{}; // 0=global, 1=no_ctxsw, 2=separate_buffer, 3=part_of_regular_buf
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(IoctlZCullBind) == 16, "IoctlZCullBind is incorrect size");
struct IoctlSetErrorNotifier {
- u64_le offset;
- u64_le size;
- u32_le mem; // nvmap object handle
+ u64_le offset{};
+ u64_le size{};
+ u32_le mem{}; // nvmap object handle
INSERT_PADDING_WORDS(1);
};
static_assert(sizeof(IoctlSetErrorNotifier) == 24, "IoctlSetErrorNotifier is incorrect size");
struct IoctlChannelSetPriority {
- u32_le priority;
+ u32_le priority{};
};
static_assert(sizeof(IoctlChannelSetPriority) == 4,
"IoctlChannelSetPriority is incorrect size");
struct IoctlSetTimeslice {
- u32_le timeslice;
+ u32_le timeslice{};
};
static_assert(sizeof(IoctlSetTimeslice) == 4, "IoctlSetTimeslice is incorrect size");
struct IoctlEventIdControl {
- u32_le cmd; // 0=disable, 1=enable, 2=clear
- u32_le id;
+ u32_le cmd{}; // 0=disable, 1=enable, 2=clear
+ u32_le id{};
};
static_assert(sizeof(IoctlEventIdControl) == 8, "IoctlEventIdControl is incorrect size");
struct IoctlGetErrorNotification {
- u64_le timestamp;
- u32_le info32;
- u16_le info16;
- u16_le status; // always 0xFFFF
+ u64_le timestamp{};
+ u32_le info32{};
+ u16_le info16{};
+ u16_le status{}; // always 0xFFFF
};
static_assert(sizeof(IoctlGetErrorNotification) == 16,
"IoctlGetErrorNotification is incorrect size");
@@ -125,80 +107,89 @@ private:
static_assert(sizeof(Fence) == 8, "Fence is incorrect size");
struct IoctlAllocGpfifoEx {
- u32_le num_entries;
- u32_le flags;
- u32_le unk0;
- u32_le unk1;
- u32_le unk2;
- u32_le unk3;
- u32_le unk4;
- u32_le unk5;
+ u32_le num_entries{};
+ u32_le flags{};
+ u32_le unk0{};
+ u32_le unk1{};
+ u32_le unk2{};
+ u32_le unk3{};
+ u32_le unk4{};
+ u32_le unk5{};
};
static_assert(sizeof(IoctlAllocGpfifoEx) == 32, "IoctlAllocGpfifoEx is incorrect size");
struct IoctlAllocGpfifoEx2 {
- u32_le num_entries; // in
- u32_le flags; // in
- u32_le unk0; // in (1 works)
- Fence fence_out; // out
- u32_le unk1; // in
- u32_le unk2; // in
- u32_le unk3; // in
+ u32_le num_entries{}; // in
+ u32_le flags{}; // in
+ u32_le unk0{}; // in (1 works)
+ Fence fence_out{}; // out
+ u32_le unk1{}; // in
+ u32_le unk2{}; // in
+ u32_le unk3{}; // in
};
static_assert(sizeof(IoctlAllocGpfifoEx2) == 32, "IoctlAllocGpfifoEx2 is incorrect size");
struct IoctlAllocObjCtx {
- u32_le class_num; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
- // 0xB06F=channel_gpfifo
- u32_le flags;
- u64_le obj_id; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
+ u32_le class_num{}; // 0x902D=2d, 0xB197=3d, 0xB1C0=compute, 0xA140=kepler, 0xB0B5=DMA,
+ // 0xB06F=channel_gpfifo
+ u32_le flags{};
+ u64_le obj_id{}; // (ignored) used for FREE_OBJ_CTX ioctl, which is not supported
};
static_assert(sizeof(IoctlAllocObjCtx) == 16, "IoctlAllocObjCtx is incorrect size");
struct IoctlSubmitGpfifo {
- u64_le address; // pointer to gpfifo entry structs
- u32_le num_entries; // number of fence objects being submitted
+ u64_le address{}; // pointer to gpfifo entry structs
+ u32_le num_entries{}; // number of fence objects being submitted
union {
u32_le raw;
BitField<0, 1, u32_le> add_wait; // append a wait sync_point to the list
BitField<1, 1, u32_le> add_increment; // append an increment to the list
- BitField<2, 1, u32_le> new_hw_format; // Mostly ignored
+ BitField<2, 1, u32_le> new_hw_format; // mostly ignored
+ BitField<4, 1, u32_le> suppress_wfi; // suppress wait for interrupt
BitField<8, 1, u32_le> increment; // increment the returned fence
} flags;
- Fence fence_out; // returned new fence object for others to wait on
+ Fence fence_out{}; // returned new fence object for others to wait on
+
+ u32 AddIncrementValue() const {
+ return flags.add_increment.Value() << 1;
+ }
};
static_assert(sizeof(IoctlSubmitGpfifo) == 16 + sizeof(Fence),
"IoctlSubmitGpfifo is incorrect size");
struct IoctlGetWaitbase {
- u32 unknown; // seems to be ignored? Nintendo added this
- u32 value;
+ u32 unknown{}; // seems to be ignored? Nintendo added this
+ u32 value{};
};
static_assert(sizeof(IoctlGetWaitbase) == 8, "IoctlGetWaitbase is incorrect size");
- u32_le nvmap_fd{};
+ s32_le nvmap_fd{};
u64_le user_data{};
IoctlZCullBind zcull_params{};
u32_le channel_priority{};
u32_le channel_timeslice{};
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
- u32 GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
- u32 AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
- u32 AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
- u32 SubmitGPFIFO(const std::vector<u8>& input, std::vector<u8>& output);
- u32 KickoffPB(const std::vector<u8>& input, std::vector<u8>& output,
- const std::vector<u8>& input2, IoctlVersion version);
- u32 GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
- u32 ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetClientData(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetClientData(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ZCullBind(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetErrorNotifier(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetChannelPriority(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult AllocGPFIFOEx2(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult AllocateObjectContext(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SubmitGPFIFOImpl(IoctlSubmitGpfifo& params, std::vector<u8>& output,
+ Tegra::CommandList&& entries);
+ NvResult SubmitGPFIFOBase(const std::vector<u8>& input, std::vector<u8>& output,
+ bool kickoff = false);
+ NvResult SubmitGPFIFOBase(const std::vector<u8>& input, const std::vector<u8>& input_inline,
+ std::vector<u8>& output);
+ NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ChannelSetTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult ChannelSetTimeslice(const std::vector<u8>& input, std::vector<u8>& output);
std::shared_ptr<nvmap> nvmap_dev;
- u32 assigned_syncpoints{};
+ SyncpointManager& syncpoint_manager;
+ Fence channel_fence;
};
} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
index bdae8b887..d8735491c 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.cpp
@@ -2,39 +2,71 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
-
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_nvdec.h"
+#include "video_core/memory_manager.h"
+#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
-nvhost_nvdec::nvhost_nvdec(Core::System& system) : nvdevice(system) {}
+nvhost_nvdec::nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
nvhost_nvdec::~nvhost_nvdec() = default;
-u32 nvhost_nvdec::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
+NvResult nvhost_nvdec::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x1:
+ return Submit(input, output);
+ case 0x2:
+ return GetSyncpoint(input, output);
+ case 0x3:
+ return GetWaitbase(input, output);
+ case 0x7:
+ return SetSubmitTimeout(input, output);
+ case 0x9:
+ return MapBuffer(input, output);
+ case 0xa: {
+ if (command.length == 0x1c) {
+ LOG_INFO(Service_NVDRV, "NVDEC video stream ended");
+ Tegra::ChCommandHeaderList cmdlist(1);
+ cmdlist[0] = Tegra::ChCommandHeader{0xDEADB33F};
+ system.GPU().PushCommandBuffer(cmdlist);
+ }
+ return UnmapBuffer(input, output);
+ }
+ default:
+ break;
+ }
+ break;
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input);
+ default:
+ break;
+ }
+ break;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_nvdec::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
- IoctlSetNvmapFD params{};
- std::memcpy(&params, input.data(), input.size());
- LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+NvResult nvhost_nvdec::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- nvmap_fd = params.nvmap_fd;
- return 0;
+NvResult nvhost_nvdec::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
} // 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 cbdac8069..79b8b6de1 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec.h
@@ -4,35 +4,21 @@
#pragma once
-#include <vector>
-#include "common/common_types.h"
-#include "common/swap.h"
-#include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include <memory>
+#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
namespace Service::Nvidia::Devices {
-class nvhost_nvdec final : public nvdevice {
+class nvhost_nvdec final : public nvhost_nvdec_common {
public:
- explicit nvhost_nvdec(Core::System& system);
+ explicit nvhost_nvdec(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
~nvhost_nvdec() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
-
-private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- };
-
- struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
- };
- static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
-
- u32_le nvmap_fd{};
-
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
};
} // 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
new file mode 100644
index 000000000..b49cecb42
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.cpp
@@ -0,0 +1,229 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <cstring>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "common/logging/log.h"
+#include "core/core.h"
+#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
+#include "core/hle/service/nvdrv/devices/nvmap.h"
+#include "core/memory.h"
+#include "video_core/memory_manager.h"
+#include "video_core/renderer_base.h"
+
+namespace Service::Nvidia::Devices {
+
+namespace {
+// Splice vectors will copy count amount of type T from the input vector into the dst vector.
+template <typename T>
+std::size_t SpliceVectors(const std::vector<u8>& input, std::vector<T>& dst, std::size_t count,
+ std::size_t offset) {
+ std::memcpy(dst.data(), input.data() + offset, count * sizeof(T));
+ offset += count * sizeof(T);
+ return offset;
+}
+
+// Write vectors will write data to the output buffer
+template <typename T>
+std::size_t WriteVectors(std::vector<u8>& dst, const std::vector<T>& src, std::size_t offset) {
+ std::memcpy(dst.data() + offset, src.data(), src.size() * sizeof(T));
+ offset += src.size() * sizeof(T);
+ return offset;
+}
+} // Anonymous namespace
+
+nvhost_nvdec_common::nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvdevice(system), nvmap_dev(std::move(nvmap_dev)) {}
+nvhost_nvdec_common::~nvhost_nvdec_common() = default;
+
+NvResult nvhost_nvdec_common::SetNVMAPfd(const std::vector<u8>& input) {
+ IoctlSetNvmapFD params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlSetNvmapFD));
+ LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+
+ nvmap_fd = params.nvmap_fd;
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::Submit(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlSubmit params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlSubmit));
+ LOG_DEBUG(Service_NVDRV, "called NVDEC Submit, cmd_buffer_count={}", params.cmd_buffer_count);
+
+ // Instantiate param buffers
+ std::size_t offset = sizeof(IoctlSubmit);
+ std::vector<CommandBuffer> command_buffers(params.cmd_buffer_count);
+ std::vector<Reloc> relocs(params.relocation_count);
+ std::vector<u32> reloc_shifts(params.relocation_count);
+ std::vector<SyncptIncr> syncpt_increments(params.syncpoint_count);
+ std::vector<SyncptIncr> wait_checks(params.syncpoint_count);
+ std::vector<Fence> fences(params.fence_count);
+
+ // Splice input into their respective buffers
+ offset = SpliceVectors(input, command_buffers, params.cmd_buffer_count, offset);
+ offset = SpliceVectors(input, relocs, params.relocation_count, offset);
+ offset = SpliceVectors(input, reloc_shifts, params.relocation_count, offset);
+ offset = SpliceVectors(input, syncpt_increments, params.syncpoint_count, offset);
+ offset = SpliceVectors(input, wait_checks, params.syncpoint_count, offset);
+ offset = SpliceVectors(input, fences, params.fence_count, offset);
+
+ // TODO(ameerj): For async gpu, utilize fences for syncpoint 'max' increment
+
+ auto& gpu = system.GPU();
+
+ for (const auto& cmd_buffer : command_buffers) {
+ auto object = nvmap_dev->GetObject(cmd_buffer.memory_id);
+ ASSERT_OR_EXECUTE(object, return NvResult::InvalidState;);
+ const auto map = FindBufferMap(object->dma_map_addr);
+ if (!map) {
+ LOG_ERROR(Service_NVDRV, "Tried to submit an invalid offset 0x{:X} dma 0x{:X}",
+ object->addr, object->dma_map_addr);
+ return NvResult::Success;
+ }
+ Tegra::ChCommandHeaderList cmdlist(cmd_buffer.word_count);
+ gpu.MemoryManager().ReadBlock(map->StartAddr() + cmd_buffer.offset, cmdlist.data(),
+ cmdlist.size() * sizeof(u32));
+ gpu.PushCommandBuffer(cmdlist);
+ }
+
+ std::memcpy(output.data(), &params, sizeof(IoctlSubmit));
+ // Some games expect command_buffers to be written back
+ offset = sizeof(IoctlSubmit);
+ offset = WriteVectors(output, command_buffers, offset);
+ offset = WriteVectors(output, relocs, offset);
+ offset = WriteVectors(output, reloc_shifts, offset);
+ offset = WriteVectors(output, syncpt_increments, offset);
+ offset = WriteVectors(output, wait_checks, offset);
+
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlGetSyncpoint params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlGetSyncpoint));
+ LOG_DEBUG(Service_NVDRV, "called GetSyncpoint, id={}", params.param);
+
+ // We found that implementing this causes deadlocks with async gpu, along with degraded
+ // performance. TODO: RE the nvdec async implementation
+ params.value = 0;
+ std::memcpy(output.data(), &params, sizeof(IoctlGetSyncpoint));
+
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlGetWaitbase params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlGetWaitbase));
+ params.value = 0; // Seems to be hard coded at 0
+ std::memcpy(output.data(), &params, sizeof(IoctlGetWaitbase));
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::MapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlMapBuffer params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
+ std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
+
+ SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
+
+ auto& gpu = system.GPU();
+
+ for (auto& cmf_buff : cmd_buffer_handles) {
+ auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
+ if (!object) {
+ LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::InvalidState;
+ }
+ if (object->dma_map_addr == 0) {
+ // NVDEC and VIC memory is in the 32-bit address space
+ // MapAllocate32 will attempt to map a lower 32-bit value in the shared gpu memory space
+ const GPUVAddr low_addr = gpu.MemoryManager().MapAllocate32(object->addr, object->size);
+ object->dma_map_addr = static_cast<u32>(low_addr);
+ // Ensure that the dma_map_addr is indeed in the lower 32-bit address space.
+ ASSERT(object->dma_map_addr == low_addr);
+ }
+ if (!object->dma_map_addr) {
+ LOG_ERROR(Service_NVDRV, "failed to map size={}", object->size);
+ } else {
+ cmf_buff.map_address = object->dma_map_addr;
+ AddBufferMap(object->dma_map_addr, object->size, object->addr,
+ object->status == nvmap::Object::Status::Allocated);
+ }
+ }
+ std::memcpy(output.data(), &params, sizeof(IoctlMapBuffer));
+ std::memcpy(output.data() + sizeof(IoctlMapBuffer), cmd_buffer_handles.data(),
+ cmd_buffer_handles.size() * sizeof(MapBufferEntry));
+
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output) {
+ IoctlMapBuffer params{};
+ std::memcpy(&params, input.data(), sizeof(IoctlMapBuffer));
+ std::vector<MapBufferEntry> cmd_buffer_handles(params.num_entries);
+ SpliceVectors(input, cmd_buffer_handles, params.num_entries, sizeof(IoctlMapBuffer));
+
+ auto& gpu = system.GPU();
+
+ for (auto& cmf_buff : cmd_buffer_handles) {
+ const auto object{nvmap_dev->GetObject(cmf_buff.map_handle)};
+ if (!object) {
+ LOG_ERROR(Service_NVDRV, "invalid cmd_buffer nvmap_handle={:X}", cmf_buff.map_handle);
+ std::memcpy(output.data(), &params, output.size());
+ return NvResult::InvalidState;
+ }
+ if (const auto size{RemoveBufferMap(object->dma_map_addr)}; size) {
+ gpu.MemoryManager().Unmap(object->dma_map_addr, *size);
+ } else {
+ // This occurs quite frequently, however does not seem to impact functionality
+ LOG_DEBUG(Service_NVDRV, "invalid offset=0x{:X} dma=0x{:X}", object->addr,
+ object->dma_map_addr);
+ }
+ object->dma_map_addr = 0;
+ }
+ std::memset(output.data(), 0, output.size());
+ return NvResult::Success;
+}
+
+NvResult nvhost_nvdec_common::SetSubmitTimeout(const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ std::memcpy(&submit_timeout, input.data(), input.size());
+ LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ return NvResult::Success;
+}
+
+std::optional<nvhost_nvdec_common::BufferMap> nvhost_nvdec_common::FindBufferMap(
+ GPUVAddr gpu_addr) const {
+ const auto it = std::find_if(
+ buffer_mappings.begin(), buffer_mappings.upper_bound(gpu_addr), [&](const auto& entry) {
+ return (gpu_addr >= entry.second.StartAddr() && gpu_addr < entry.second.EndAddr());
+ });
+
+ ASSERT(it != buffer_mappings.end());
+ return it->second;
+}
+
+void nvhost_nvdec_common::AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated) {
+ buffer_mappings.insert_or_assign(gpu_addr, BufferMap{gpu_addr, size, cpu_addr, is_allocated});
+}
+
+std::optional<std::size_t> nvhost_nvdec_common::RemoveBufferMap(GPUVAddr gpu_addr) {
+ const auto iter{buffer_mappings.find(gpu_addr)};
+ if (iter == buffer_mappings.end()) {
+ return std::nullopt;
+ }
+ std::size_t size = 0;
+ if (iter->second.IsAllocated()) {
+ size = iter->second.Size();
+ }
+ buffer_mappings.erase(iter);
+ return size;
+}
+
+} // namespace Service::Nvidia::Devices
diff --git a/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
new file mode 100644
index 000000000..86ba3a4d1
--- /dev/null
+++ b/src/core/hle/service/nvdrv/devices/nvhost_nvdec_common.h
@@ -0,0 +1,196 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <map>
+#include <vector>
+#include "common/common_types.h"
+#include "common/swap.h"
+#include "core/hle/service/nvdrv/devices/nvdevice.h"
+
+namespace Service::Nvidia::Devices {
+class nvmap;
+
+class nvhost_nvdec_common : public nvdevice {
+public:
+ explicit nvhost_nvdec_common(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ ~nvhost_nvdec_common() override;
+
+ /**
+ * Handles an ioctl1 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param output A buffer where the output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) = 0;
+
+ /**
+ * Handles an ioctl2 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param inline_input A buffer containing the input data for the ioctl which has been inlined.
+ * @param output A buffer where the output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) = 0;
+
+ /**
+ * Handles an ioctl3 request.
+ * @param command The ioctl command id.
+ * @param input A buffer containing the input data for the ioctl.
+ * @param output A buffer where the output data will be written to.
+ * @param inline_output A buffer where the inlined output data will be written to.
+ * @returns The result code of the ioctl.
+ */
+ virtual NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) = 0;
+
+protected:
+ class BufferMap final {
+ public:
+ constexpr BufferMap() = default;
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size)
+ : start_addr{start_addr}, end_addr{start_addr + size} {}
+
+ constexpr BufferMap(GPUVAddr start_addr, std::size_t size, VAddr cpu_addr,
+ bool is_allocated)
+ : start_addr{start_addr}, end_addr{start_addr + size}, cpu_addr{cpu_addr},
+ is_allocated{is_allocated} {}
+
+ constexpr VAddr StartAddr() const {
+ return start_addr;
+ }
+
+ constexpr VAddr EndAddr() const {
+ return end_addr;
+ }
+
+ constexpr std::size_t Size() const {
+ return end_addr - start_addr;
+ }
+
+ constexpr VAddr CpuAddr() const {
+ return cpu_addr;
+ }
+
+ constexpr bool IsAllocated() const {
+ return is_allocated;
+ }
+
+ private:
+ GPUVAddr start_addr{};
+ GPUVAddr end_addr{};
+ VAddr cpu_addr{};
+ bool is_allocated{};
+ };
+
+ struct IoctlSetNvmapFD {
+ s32_le nvmap_fd{};
+ };
+ static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
+
+ struct IoctlSubmitCommandBuffer {
+ u32_le id{};
+ u32_le offset{};
+ u32_le count{};
+ };
+ static_assert(sizeof(IoctlSubmitCommandBuffer) == 0xC,
+ "IoctlSubmitCommandBuffer is incorrect size");
+ struct IoctlSubmit {
+ u32_le cmd_buffer_count{};
+ u32_le relocation_count{};
+ u32_le syncpoint_count{};
+ u32_le fence_count{};
+ };
+ static_assert(sizeof(IoctlSubmit) == 0x10, "IoctlSubmit has incorrect size");
+
+ struct CommandBuffer {
+ s32 memory_id{};
+ u32 offset{};
+ s32 word_count{};
+ };
+ static_assert(sizeof(CommandBuffer) == 0xC, "CommandBuffer has incorrect size");
+
+ struct Reloc {
+ s32 cmdbuffer_memory{};
+ s32 cmdbuffer_offset{};
+ s32 target{};
+ s32 target_offset{};
+ };
+ static_assert(sizeof(Reloc) == 0x10, "CommandBuffer has incorrect size");
+
+ struct SyncptIncr {
+ u32 id{};
+ u32 increments{};
+ };
+ static_assert(sizeof(SyncptIncr) == 0x8, "CommandBuffer has incorrect size");
+
+ struct Fence {
+ u32 id{};
+ u32 value{};
+ };
+ static_assert(sizeof(Fence) == 0x8, "CommandBuffer has incorrect size");
+
+ struct IoctlGetSyncpoint {
+ // Input
+ u32_le param{};
+ // Output
+ u32_le value{};
+ };
+ static_assert(sizeof(IoctlGetSyncpoint) == 8, "IocGetIdParams has wrong size");
+
+ struct IoctlGetWaitbase {
+ u32_le unknown{}; // seems to be ignored? Nintendo added this
+ u32_le value{};
+ };
+ static_assert(sizeof(IoctlGetWaitbase) == 0x8, "IoctlGetWaitbase is incorrect size");
+
+ struct IoctlMapBuffer {
+ u32_le num_entries{};
+ u32_le data_address{}; // Ignored by the driver.
+ u32_le attach_host_ch_das{};
+ };
+ static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
+
+ struct IocGetIdParams {
+ // Input
+ u32_le param{};
+ // Output
+ u32_le value{};
+ };
+ static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
+
+ // Used for mapping and unmapping command buffers
+ struct MapBufferEntry {
+ u32_le map_handle{};
+ u32_le map_address{};
+ };
+ static_assert(sizeof(IoctlMapBuffer) == 0x0C, "IoctlMapBuffer is incorrect size");
+
+ /// Ioctl command implementations
+ NvResult SetNVMAPfd(const std::vector<u8>& input);
+ NvResult Submit(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetSyncpoint(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult GetWaitbase(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult MapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult UnmapBuffer(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetSubmitTimeout(const std::vector<u8>& input, std::vector<u8>& output);
+
+ std::optional<BufferMap> FindBufferMap(GPUVAddr gpu_addr) const;
+ void AddBufferMap(GPUVAddr gpu_addr, std::size_t size, VAddr cpu_addr, bool is_allocated);
+ std::optional<std::size_t> RemoveBufferMap(GPUVAddr gpu_addr);
+
+ s32_le nvmap_fd{};
+ u32_le submit_timeout{};
+ std::shared_ptr<nvmap> nvmap_dev;
+
+ // This is expected to be ordered, therefore we must use a map, not unordered_map
+ std::map<GPUVAddr, BufferMap> buffer_mappings;
+};
+}; // namespace Service::Nvidia::Devices
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 {
nvhost_nvjpg::nvhost_nvjpg(Core::System& system) : nvdevice(system) {}
nvhost_nvjpg::~nvhost_nvjpg() = default;
-u32 nvhost_nvjpg::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
+NvResult nvhost_nvjpg::Ioctl1(Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ switch (command.group) {
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input, output);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvhost_nvjpg::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvjpg::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvhost_nvjpg::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
IoctlSetNvmapFD params{};
std::memcpy(&params, input.data(), input.size());
LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
nvmap_fd = params.nvmap_fd;
- return 0;
+ return NvResult::Success;
}
} // 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:
explicit nvhost_nvjpg(Core::System& system);
~nvhost_nvjpg() override;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- };
-
struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
+ s32_le nvmap_fd{};
};
static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
- u32_le nvmap_fd{};
+ s32_le nvmap_fd{};
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
};
} // 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 c695b8863..805fe86ae 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.cpp
@@ -2,39 +2,63 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <cstring>
-
#include "common/assert.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/service/nvdrv/devices/nvhost_vic.h"
+#include "video_core/memory_manager.h"
+#include "video_core/renderer_base.h"
namespace Service::Nvidia::Devices {
+nvhost_vic::nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev)
+ : nvhost_nvdec_common(system, std::move(nvmap_dev)) {}
-nvhost_vic::nvhost_vic(Core::System& system) : nvdevice(system) {}
nvhost_vic::~nvhost_vic() = default;
-u32 nvhost_vic::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- LOG_DEBUG(Service_NVDRV, "called, command=0x{:08X}, input_size=0x{:X}, output_size=0x{:X}",
- command.raw, input.size(), output.size());
-
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::IocSetNVMAPfdCommand:
- return SetNVMAPfd(input, output);
+NvResult nvhost_vic::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x0:
+ switch (command.cmd) {
+ case 0x1:
+ return Submit(input, output);
+ case 0x2:
+ return GetSyncpoint(input, output);
+ case 0x3:
+ return GetWaitbase(input, output);
+ case 0x9:
+ return MapBuffer(input, output);
+ case 0xa:
+ return UnmapBuffer(input, output);
+ default:
+ break;
+ }
+ break;
+ case 'H':
+ switch (command.cmd) {
+ case 0x1:
+ return SetNVMAPfd(input);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
}
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
-u32 nvhost_vic::SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output) {
- IoctlSetNvmapFD params{};
- std::memcpy(&params, input.data(), input.size());
- LOG_DEBUG(Service_NVDRV, "called, fd={}", params.nvmap_fd);
+NvResult nvhost_vic::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
- nvmap_fd = params.nvmap_fd;
- return 0;
+NvResult nvhost_vic::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
}
} // 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 bec32bea1..b2e11f4d4 100644
--- a/src/core/hle/service/nvdrv/devices/nvhost_vic.h
+++ b/src/core/hle/service/nvdrv/devices/nvhost_vic.h
@@ -4,35 +4,20 @@
#pragma once
-#include <vector>
-#include "common/common_types.h"
-#include "common/swap.h"
-#include "core/hle/service/nvdrv/devices/nvdevice.h"
+#include "core/hle/service/nvdrv/devices/nvhost_nvdec_common.h"
namespace Service::Nvidia::Devices {
+class nvmap;
-class nvhost_vic final : public nvdevice {
+class nvhost_vic final : public nvhost_nvdec_common {
public:
- explicit nvhost_vic(Core::System& system);
- ~nvhost_vic() override;
-
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
-
-private:
- enum class IoctlCommand : u32_le {
- IocSetNVMAPfdCommand = 0x40044801,
- };
-
- struct IoctlSetNvmapFD {
- u32_le nvmap_fd;
- };
- static_assert(sizeof(IoctlSetNvmapFD) == 4, "IoctlSetNvmapFD is incorrect size");
-
- u32_le nvmap_fd{};
-
- u32 SetNVMAPfd(const std::vector<u8>& input, std::vector<u8>& output);
+ explicit nvhost_vic(Core::System& system, std::shared_ptr<nvmap> nvmap_dev);
+ ~nvhost_vic();
+
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
};
-
} // 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 @@
namespace Service::Nvidia::Devices {
-namespace NvErrCodes {
-enum {
- OperationNotPermitted = -1,
- InvalidValue = -22,
-};
-}
-
nvmap::nvmap(Core::System& system) : nvdevice(system) {
// Handle 0 appears to be used when remapping, so we create a placeholder empty nvmap object to
// represent this.
@@ -26,6 +19,46 @@ nvmap::nvmap(Core::System& system) : nvdevice(system) {
nvmap::~nvmap() = default;
+NvResult nvmap::Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) {
+ switch (command.group) {
+ case 0x1:
+ switch (command.cmd) {
+ case 0x1:
+ return IocCreate(input, output);
+ case 0x3:
+ return IocFromId(input, output);
+ case 0x4:
+ return IocAlloc(input, output);
+ case 0x5:
+ return IocFree(input, output);
+ case 0x9:
+ return IocParam(input, output);
+ case 0xe:
+ return IocGetId(input, output);
+ default:
+ break;
+ }
+ break;
+ default:
+ break;
+ }
+
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvmap::Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
+NvResult nvmap::Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) {
+ UNIMPLEMENTED_MSG("Unimplemented ioctl={:08X}", command.raw);
+ return NvResult::NotImplemented;
+}
+
VAddr nvmap::GetObjectAddress(u32 handle) const {
auto object = GetObject(handle);
ASSERT(object);
@@ -33,28 +66,6 @@ VAddr nvmap::GetObjectAddress(u32 handle) const {
return object->addr;
}
-u32 nvmap::ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- switch (static_cast<IoctlCommand>(command.raw)) {
- case IoctlCommand::Create:
- return IocCreate(input, output);
- case IoctlCommand::Alloc:
- return IocAlloc(input, output);
- case IoctlCommand::GetId:
- return IocGetId(input, output);
- case IoctlCommand::FromId:
- return IocFromId(input, output);
- case IoctlCommand::Param:
- return IocParam(input, output);
- case IoctlCommand::Free:
- return IocFree(input, output);
- }
-
- UNIMPLEMENTED_MSG("Unimplemented ioctl");
- return 0;
-}
-
u32 nvmap::CreateObject(u32 size) {
// Create a new nvmap object and obtain a handle to it.
auto object = std::make_shared<Object>();
@@ -70,35 +81,35 @@ u32 nvmap::CreateObject(u32 size) {
return handle;
}
-u32 nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocCreate(const std::vector<u8>& input, std::vector<u8>& output) {
IocCreateParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "size=0x{:08X}", params.size);
if (!params.size) {
LOG_ERROR(Service_NVDRV, "Size is 0");
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
params.handle = CreateObject(params.size);
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
IocAllocParams params;
std::memcpy(&params, input.data(), sizeof(params));
LOG_DEBUG(Service_NVDRV, "called, addr={:X}", params.addr);
if (!params.handle) {
LOG_ERROR(Service_NVDRV, "Handle is 0");
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if ((params.align - 1) & params.align) {
LOG_ERROR(Service_NVDRV, "Incorrect alignment used, alignment={:08X}", params.align);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
const u32 min_alignment = 0x1000;
@@ -109,12 +120,12 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if (object->status == Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is already allocated, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ return NvResult::InsufficientMemory;
}
object->flags = params.flags;
@@ -124,10 +135,10 @@ u32 nvmap::IocAlloc(const std::vector<u8>& input, std::vector<u8>& output) {
object->status = Object::Status::Allocated;
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
IocGetIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -135,22 +146,22 @@ u32 nvmap::IocGetId(const std::vector<u8>& input, std::vector<u8>& output) {
if (!params.handle) {
LOG_ERROR(Service_NVDRV, "Handle is zero");
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ return NvResult::BadValue;
}
params.id = object->id;
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
IocFromIdParams params;
std::memcpy(&params, input.data(), sizeof(params));
@@ -160,13 +171,13 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
[&](const auto& entry) { return entry.second->id == params.id; });
if (itr == handles.end()) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
auto& object = itr->second;
if (object->status != Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
itr->second->refcount++;
@@ -175,10 +186,10 @@ u32 nvmap::IocFromId(const std::vector<u8>& input, std::vector<u8>& output) {
params.handle = itr->first;
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
enum class ParamTypes { Size = 1, Alignment = 2, Base = 3, Heap = 4, Kind = 5, Compr = 6 };
IocParamParams params;
@@ -189,12 +200,12 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
auto object = GetObject(params.handle);
if (!object) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if (object->status != Object::Status::Allocated) {
LOG_ERROR(Service_NVDRV, "Object is not allocated, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::OperationNotPermitted);
+ return NvResult::BadValue;
}
switch (static_cast<ParamTypes>(params.param)) {
@@ -216,10 +227,10 @@ u32 nvmap::IocParam(const std::vector<u8>& input, std::vector<u8>& output) {
}
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
-u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
+NvResult nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
// TODO(Subv): These flags are unconfirmed.
enum FreeFlags {
Freed = 0,
@@ -234,14 +245,14 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
auto itr = handles.find(params.handle);
if (itr == handles.end()) {
LOG_ERROR(Service_NVDRV, "Object does not exist, handle={:08X}", params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
if (!itr->second->refcount) {
LOG_ERROR(
Service_NVDRV,
"There is no references to this object. The object is already freed. handle={:08X}",
params.handle);
- return static_cast<u32>(NvErrCodes::InvalidValue);
+ return NvResult::BadValue;
}
itr->second->refcount--;
@@ -261,7 +272,7 @@ u32 nvmap::IocFree(const std::vector<u8>& input, std::vector<u8>& output) {
handles.erase(params.handle);
std::memcpy(output.data(), &params, sizeof(params));
- return 0;
+ return NvResult::Success;
}
} // 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 84624be00..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:
explicit nvmap(Core::System& system);
~nvmap() override;
+ NvResult Ioctl1(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output) override;
+ NvResult Ioctl2(Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) override;
+ NvResult Ioctl3(Ioctl command, const std::vector<u8>& input, std::vector<u8>& output,
+ std::vector<u8>& inline_output) override;
+
/// Returns the allocated address of an nvmap object given its handle.
VAddr GetObjectAddress(u32 handle) const;
- u32 ioctl(Ioctl command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) override;
-
/// Represents an nvmap object.
struct Object {
enum class Status { Created, Allocated };
@@ -37,6 +39,7 @@ public:
VAddr addr;
Status status;
u32 refcount;
+ u32 dma_map_addr;
};
std::shared_ptr<Object> GetObject(u32 handle) const {
@@ -57,76 +60,68 @@ private:
/// Mapping of currently allocated handles to the objects they represent.
std::unordered_map<u32, std::shared_ptr<Object>> handles;
- enum class IoctlCommand : u32 {
- Create = 0xC0080101,
- FromId = 0xC0080103,
- Alloc = 0xC0200104,
- Free = 0xC0180105,
- Param = 0xC00C0109,
- GetId = 0xC008010E,
- };
struct IocCreateParams {
// Input
- u32_le size;
+ u32_le size{};
// Output
- u32_le handle;
+ u32_le handle{};
};
static_assert(sizeof(IocCreateParams) == 8, "IocCreateParams has wrong size");
struct IocFromIdParams {
// Input
- u32_le id;
+ u32_le id{};
// Output
- u32_le handle;
+ u32_le handle{};
};
static_assert(sizeof(IocFromIdParams) == 8, "IocFromIdParams has wrong size");
struct IocAllocParams {
// Input
- u32_le handle;
- u32_le heap_mask;
- u32_le flags;
- u32_le align;
- u8 kind;
+ u32_le handle{};
+ u32_le heap_mask{};
+ u32_le flags{};
+ u32_le align{};
+ u8 kind{};
INSERT_PADDING_BYTES(7);
- u64_le addr;
+ u64_le addr{};
};
static_assert(sizeof(IocAllocParams) == 32, "IocAllocParams has wrong size");
struct IocFreeParams {
- u32_le handle;
+ u32_le handle{};
INSERT_PADDING_BYTES(4);
- u64_le address;
- u32_le size;
- u32_le flags;
+ u64_le address{};
+ u32_le size{};
+ u32_le flags{};
};
static_assert(sizeof(IocFreeParams) == 24, "IocFreeParams has wrong size");
struct IocParamParams {
// Input
- u32_le handle;
- u32_le param;
+ u32_le handle{};
+ u32_le param{};
// Output
- u32_le result;
+ u32_le result{};
};
static_assert(sizeof(IocParamParams) == 12, "IocParamParams has wrong size");
struct IocGetIdParams {
// Output
- u32_le id;
+ u32_le id{};
// Input
- u32_le handle;
+ u32_le handle{};
};
static_assert(sizeof(IocGetIdParams) == 8, "IocGetIdParams has wrong size");
u32 CreateObject(u32 size);
- u32 IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocParam(const std::vector<u8>& input, std::vector<u8>& output);
- u32 IocFree(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocCreate(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocAlloc(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocGetId(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocFromId(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocParam(const std::vector<u8>& input, std::vector<u8>& output);
+ NvResult IocFree(const std::vector<u8>& input, std::vector<u8>& output);
};
} // 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) {
void NVDRV::Open(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
const auto& buffer = ctx.ReadBuffer();
- std::string device_name(buffer.begin(), buffer.end());
+ const std::string device_name(buffer.begin(), buffer.end());
+ DeviceFD fd = nvdrv->Open(device_name);
- u32 fd = nvdrv->Open(device_name);
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(fd);
- rb.Push<u32>(0);
+ rb.Push<DeviceFD>(fd);
+ rb.PushEnum(fd != INVALID_NVDRV_FD ? NvResult::Success : NvResult::FileOperationFailed);
+}
+
+void NVDRV::ServiceError(Kernel::HLERequestContext& ctx, NvResult result) {
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(result);
}
-void NVDRV::IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version) {
+void NVDRV::Ioctl1(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u32 fd = rp.Pop<u32>();
- u32 command = rp.Pop<u32>();
-
- /// Ioctl 3 has 2 outputs, first in the input params, second is the result
- std::vector<u8> output(ctx.GetWriteBufferSize(0));
- std::vector<u8> output2;
- if (version == IoctlVersion::Version3) {
- output2.resize((ctx.GetWriteBufferSize(1)));
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto command = rp.PopRaw<Ioctl>();
+ LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
}
- /// Ioctl2 has 2 inputs. It's used to pass data directly instead of providing a pointer.
- /// KickOfPB uses this
- auto input = ctx.ReadBuffer(0);
+ // Check device
+ std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+ const auto input_buffer = ctx.ReadBuffer(0);
- std::vector<u8> input2;
- if (version == IoctlVersion::Version2) {
- input2 = ctx.ReadBuffer(1);
- }
+ const auto nv_result = nvdrv->Ioctl1(fd, command, input_buffer, output_buffer);
- IoctlCtrl ctrl{};
-
- u32 result = nvdrv->Ioctl(fd, command, input, input2, output, output2, ctrl, version);
-
- if (ctrl.must_delay) {
- ctrl.fresh_call = false;
- ctx.SleepClientThread(
- "NVServices::DelayedResponse", ctrl.timeout,
- [=, this](std::shared_ptr<Kernel::Thread> thread, Kernel::HLERequestContext& ctx_,
- Kernel::ThreadWakeupReason reason) {
- IoctlCtrl ctrl2{ctrl};
- std::vector<u8> tmp_output = output;
- std::vector<u8> tmp_output2 = output2;
- const u32 ioctl_result = nvdrv->Ioctl(fd, command, input, input2, tmp_output,
- tmp_output2, ctrl2, version);
- ctx_.WriteBuffer(tmp_output, 0);
- if (version == IoctlVersion::Version3) {
- ctx_.WriteBuffer(tmp_output2, 1);
- }
- IPC::ResponseBuilder rb{ctx_, 3};
- rb.Push(RESULT_SUCCESS);
- rb.Push(ioctl_result);
- },
- nvdrv->GetEventWriteable(ctrl.event_id));
- } else {
- ctx.WriteBuffer(output);
- if (version == IoctlVersion::Version3) {
- ctx.WriteBuffer(output2, 1);
- }
+ if (command.is_out != 0) {
+ ctx.WriteBuffer(output_buffer);
}
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push(result);
-}
-
-void NVDRV::Ioctl(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NVDRV, "called");
- IoctlBase(ctx, IoctlVersion::Version1);
+ rb.PushEnum(nv_result);
}
void NVDRV::Ioctl2(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NVDRV, "called");
- IoctlBase(ctx, IoctlVersion::Version2);
+ IPC::RequestParser rp{ctx};
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto command = rp.PopRaw<Ioctl>();
+ LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
+ const auto input_buffer = ctx.ReadBuffer(0);
+ const auto input_inlined_buffer = ctx.ReadBuffer(1);
+ std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+
+ const auto nv_result =
+ nvdrv->Ioctl2(fd, command, input_buffer, input_inlined_buffer, output_buffer);
+
+ if (command.is_out != 0) {
+ ctx.WriteBuffer(output_buffer);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(nv_result);
}
void NVDRV::Ioctl3(Kernel::HLERequestContext& ctx) {
- LOG_DEBUG(Service_NVDRV, "called");
- IoctlBase(ctx, IoctlVersion::Version3);
+ IPC::RequestParser rp{ctx};
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto command = rp.PopRaw<Ioctl>();
+ LOG_DEBUG(Service_NVDRV, "called fd={}, ioctl=0x{:08X}", fd, command.raw);
+
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
+ const auto input_buffer = ctx.ReadBuffer(0);
+ std::vector<u8> output_buffer(ctx.GetWriteBufferSize(0));
+ std::vector<u8> output_buffer_inline(ctx.GetWriteBufferSize(1));
+
+ const auto nv_result =
+ nvdrv->Ioctl3(fd, command, input_buffer, output_buffer, output_buffer_inline);
+
+ if (command.is_out != 0) {
+ ctx.WriteBuffer(output_buffer, 0);
+ ctx.WriteBuffer(output_buffer_inline, 1);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(nv_result);
}
void NVDRV::Close(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_NVDRV, "called");
- IPC::RequestParser rp{ctx};
- u32 fd = rp.Pop<u32>();
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
- auto result = nvdrv->Close(fd);
+ IPC::RequestParser rp{ctx};
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto result = nvdrv->Close(fd);
- IPC::ResponseBuilder rb{ctx, 2};
- rb.Push(result);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(result);
}
void NVDRV::Initialize(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
+ is_initialized = true;
+
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
+ rb.PushEnum(NvResult::Success);
}
void NVDRV::QueryEvent(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
- u32 fd = rp.Pop<u32>();
- // TODO(Blinkhawk): Figure the meaning of the flag at bit 16
- u32 event_id = rp.Pop<u32>() & 0x000000FF;
+ const auto fd = rp.Pop<DeviceFD>();
+ const auto event_id = rp.Pop<u32>() & 0x00FF;
LOG_WARNING(Service_NVDRV, "(STUBBED) called, fd={:X}, event_id={:X}", fd, event_id);
- IPC::ResponseBuilder rb{ctx, 3, 1};
- rb.Push(RESULT_SUCCESS);
+ if (!is_initialized) {
+ ServiceError(ctx, NvResult::NotInitialized);
+ LOG_ERROR(Service_NVDRV, "NvServices is not initalized!");
+ return;
+ }
+
+ const auto nv_result = nvdrv->VerifyFD(fd);
+ if (nv_result != NvResult::Success) {
+ LOG_ERROR(Service_NVDRV, "Invalid FD specified DeviceFD={}!", fd);
+ ServiceError(ctx, nv_result);
+ return;
+ }
+
if (event_id < MaxNvEvents) {
+ IPC::ResponseBuilder rb{ctx, 3, 1};
+ rb.Push(RESULT_SUCCESS);
auto event = nvdrv->GetEvent(event_id);
event->Clear();
rb.PushCopyObjects(event);
- rb.Push<u32>(NvResult::Success);
+ rb.PushEnum(NvResult::Success);
} else {
- rb.Push<u32>(0);
- rb.Push<u32>(NvResult::BadParameter);
+ IPC::ResponseBuilder rb{ctx, 3};
+ rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(NvResult::BadParameter);
}
}
@@ -151,7 +197,7 @@ void NVDRV::SetAruid(Kernel::HLERequestContext& ctx) {
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0);
+ rb.PushEnum(NvResult::Success);
}
void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx) {
@@ -164,8 +210,9 @@ void NVDRV::SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ct
void NVDRV::GetStatus(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service_NVDRV, "(STUBBED) called");
- IPC::ResponseBuilder rb{ctx, 2};
+ IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
+ rb.PushEnum(NvResult::Success);
}
void NVDRV::DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx) {
@@ -181,7 +228,7 @@ NVDRV::NVDRV(std::shared_ptr<Module> nvdrv, const char* name)
: ServiceFramework(name), nvdrv(std::move(nvdrv)) {
static const FunctionInfo functions[] = {
{0, &NVDRV::Open, "Open"},
- {1, &NVDRV::Ioctl, "Ioctl"},
+ {1, &NVDRV::Ioctl1, "Ioctl"},
{2, &NVDRV::Close, "Close"},
{3, &NVDRV::Initialize, "Initialize"},
{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:
private:
void Open(Kernel::HLERequestContext& ctx);
- void Ioctl(Kernel::HLERequestContext& ctx);
+ void Ioctl1(Kernel::HLERequestContext& ctx);
void Ioctl2(Kernel::HLERequestContext& ctx);
void Ioctl3(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
@@ -33,11 +33,13 @@ private:
void SetGraphicsFirmwareMemoryMarginEnabled(Kernel::HLERequestContext& ctx);
void GetStatus(Kernel::HLERequestContext& ctx);
void DumpGraphicsMemoryInfo(Kernel::HLERequestContext& ctx);
- void IoctlBase(Kernel::HLERequestContext& ctx, IoctlVersion version);
+
+ void ServiceError(Kernel::HLERequestContext& ctx, NvResult result);
std::shared_ptr<Module> nvdrv;
u64 pid{};
+ bool is_initialized{};
};
} // 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 @@
#pragma once
#include <array>
+#include "common/bit_field.h"
#include "common/common_types.h"
namespace Service::Nvidia {
constexpr u32 MaxSyncPoints = 192;
constexpr u32 MaxNvEvents = 64;
+using DeviceFD = s32;
+
+constexpr DeviceFD INVALID_NVDRV_FD = -1;
struct Fence {
s32 id;
@@ -20,11 +24,61 @@ struct MultiFence {
std::array<Fence, 4> fences;
};
-enum NvResult : u32 {
- Success = 0,
- BadParameter = 4,
- Timeout = 5,
- ResourceError = 15,
+enum class NvResult : u32 {
+ Success = 0x0,
+ NotImplemented = 0x1,
+ NotSupported = 0x2,
+ NotInitialized = 0x3,
+ BadParameter = 0x4,
+ Timeout = 0x5,
+ InsufficientMemory = 0x6,
+ ReadOnlyAttribute = 0x7,
+ InvalidState = 0x8,
+ InvalidAddress = 0x9,
+ InvalidSize = 0xA,
+ BadValue = 0xB,
+ AlreadyAllocated = 0xD,
+ Busy = 0xE,
+ ResourceError = 0xF,
+ CountMismatch = 0x10,
+ OverFlow = 0x11,
+ InsufficientTransferMemory = 0x1000,
+ InsufficientVideoMemory = 0x10000,
+ BadSurfaceColorScheme = 0x10001,
+ InvalidSurface = 0x10002,
+ SurfaceNotSupported = 0x10003,
+ DispInitFailed = 0x20000,
+ DispAlreadyAttached = 0x20001,
+ DispTooManyDisplays = 0x20002,
+ DispNoDisplaysAttached = 0x20003,
+ DispModeNotSupported = 0x20004,
+ DispNotFound = 0x20005,
+ DispAttachDissallowed = 0x20006,
+ DispTypeNotSupported = 0x20007,
+ DispAuthenticationFailed = 0x20008,
+ DispNotAttached = 0x20009,
+ DispSamePwrState = 0x2000A,
+ DispEdidFailure = 0x2000B,
+ DispDsiReadAckError = 0x2000C,
+ DispDsiReadInvalidResp = 0x2000D,
+ FileWriteFailed = 0x30000,
+ FileReadFailed = 0x30001,
+ EndOfFile = 0x30002,
+ FileOperationFailed = 0x30003,
+ DirOperationFailed = 0x30004,
+ EndOfDirList = 0x30005,
+ ConfigVarNotFound = 0x30006,
+ InvalidConfigVar = 0x30007,
+ LibraryNotFound = 0x30008,
+ SymbolNotFound = 0x30009,
+ MemoryMapFailed = 0x3000A,
+ IoctlFailed = 0x3000F,
+ AccessDenied = 0x30010,
+ DeviceNotFound = 0x30011,
+ KernelDriverNotFound = 0x30012,
+ FileNotFound = 0x30013,
+ PathAlreadyExists = 0x30014,
+ ModuleNotPresent = 0xA000E,
};
enum class EventState {
@@ -34,21 +88,13 @@ enum class EventState {
Busy = 3,
};
-enum class IoctlVersion : u32 {
- Version1,
- Version2,
- Version3,
-};
-
-struct IoctlCtrl {
- // First call done to the servioce for services that call itself again after a call.
- bool fresh_call{true};
- // Tells the Ioctl Wrapper that it must delay the IPC response and send the thread to sleep
- bool must_delay{};
- // Timeout for the delay
- s64 timeout{};
- // NV Event Id
- s32 event_id{-1};
+union Ioctl {
+ u32_le raw;
+ BitField<0, 8, u32> cmd;
+ BitField<8, 8, u32> group;
+ BitField<16, 14, u32> length;
+ BitField<30, 1, u32> is_in;
+ BitField<31, 1, u32> is_out;
};
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.cpp b/src/core/hle/service/nvdrv/nvdrv.cpp
index 197c77db0..bdbbedd0d 100644
--- a/src/core/hle/service/nvdrv/nvdrv.cpp
+++ b/src/core/hle/service/nvdrv/nvdrv.cpp
@@ -5,6 +5,7 @@
#include <utility>
#include <fmt/format.h>
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/readable_event.h"
#include "core/hle/kernel/writable_event.h"
@@ -21,6 +22,7 @@
#include "core/hle/service/nvdrv/interface.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvdrv/nvmemp.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/nvflinger/nvflinger.h"
namespace Service::Nvidia {
@@ -36,58 +38,125 @@ void InstallInterfaces(SM::ServiceManager& service_manager, NVFlinger::NVFlinger
nvflinger.SetNVDrvInstance(module_);
}
-Module::Module(Core::System& system) {
+Module::Module(Core::System& system) : syncpoint_manager{system.GPU()} {
auto& kernel = system.Kernel();
for (u32 i = 0; i < MaxNvEvents; i++) {
std::string event_label = fmt::format("NVDRV::NvEvent_{}", i);
- events_interface.events[i] = Kernel::WritableEvent::CreateEventPair(kernel, event_label);
+ events_interface.events[i] = {Kernel::WritableEvent::CreateEventPair(kernel, event_label)};
events_interface.status[i] = EventState::Free;
events_interface.registered[i] = false;
}
auto nvmap_dev = std::make_shared<Devices::nvmap>(system);
devices["/dev/nvhost-as-gpu"] = std::make_shared<Devices::nvhost_as_gpu>(system, nvmap_dev);
- devices["/dev/nvhost-gpu"] = std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev);
+ devices["/dev/nvhost-gpu"] =
+ std::make_shared<Devices::nvhost_gpu>(system, nvmap_dev, syncpoint_manager);
devices["/dev/nvhost-ctrl-gpu"] = std::make_shared<Devices::nvhost_ctrl_gpu>(system);
devices["/dev/nvmap"] = nvmap_dev;
devices["/dev/nvdisp_disp0"] = std::make_shared<Devices::nvdisp_disp0>(system, nvmap_dev);
- devices["/dev/nvhost-ctrl"] = std::make_shared<Devices::nvhost_ctrl>(system, events_interface);
- devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system);
+ devices["/dev/nvhost-ctrl"] =
+ std::make_shared<Devices::nvhost_ctrl>(system, events_interface, syncpoint_manager);
+ devices["/dev/nvhost-nvdec"] = std::make_shared<Devices::nvhost_nvdec>(system, nvmap_dev);
devices["/dev/nvhost-nvjpg"] = std::make_shared<Devices::nvhost_nvjpg>(system);
- devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system);
+ devices["/dev/nvhost-vic"] = std::make_shared<Devices::nvhost_vic>(system, nvmap_dev);
}
Module::~Module() = default;
-u32 Module::Open(const std::string& device_name) {
- ASSERT_MSG(devices.find(device_name) != devices.end(), "Trying to open unknown device {}",
- device_name);
+NvResult Module::VerifyFD(DeviceFD fd) const {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ if (open_files.find(fd) == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return NvResult::Success;
+}
+
+DeviceFD Module::Open(const std::string& device_name) {
+ if (devices.find(device_name) == devices.end()) {
+ LOG_ERROR(Service_NVDRV, "Trying to open unknown device {}", device_name);
+ return INVALID_NVDRV_FD;
+ }
auto device = devices[device_name];
- const u32 fd = next_fd++;
+ const DeviceFD fd = next_fd++;
open_files[fd] = std::move(device);
return fd;
}
-u32 Module::Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version) {
- auto itr = open_files.find(fd);
- ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
+NvResult Module::Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
- auto& device = itr->second;
- return device->ioctl({command}, input, input2, output, output2, ctrl, version);
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return itr->second->Ioctl1(command, input, output);
}
-ResultCode Module::Close(u32 fd) {
- auto itr = open_files.find(fd);
- ASSERT_MSG(itr != open_files.end(), "Tried to talk to an invalid device");
+NvResult Module::Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return itr->second->Ioctl2(command, input, inline_input, output);
+}
+
+NvResult Module::Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output, std::vector<u8>& inline_output) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
+
+ return itr->second->Ioctl3(command, input, output, inline_output);
+}
+
+NvResult Module::Close(DeviceFD fd) {
+ if (fd < 0) {
+ LOG_ERROR(Service_NVDRV, "Invalid DeviceFD={}!", fd);
+ return NvResult::InvalidState;
+ }
+
+ const auto itr = open_files.find(fd);
+
+ if (itr == open_files.end()) {
+ LOG_ERROR(Service_NVDRV, "Could not find DeviceFD={}!", fd);
+ return NvResult::NotImplemented;
+ }
open_files.erase(itr);
- // TODO(flerovium): return correct result code if operation failed.
- return RESULT_SUCCESS;
+ return NvResult::Success;
}
void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
@@ -95,17 +164,17 @@ void Module::SignalSyncpt(const u32 syncpoint_id, const u32 value) {
if (events_interface.assigned_syncpt[i] == syncpoint_id &&
events_interface.assigned_value[i] == value) {
events_interface.LiberateEvent(i);
- events_interface.events[i].writable->Signal();
+ events_interface.events[i].event.writable->Signal();
}
}
}
std::shared_ptr<Kernel::ReadableEvent> Module::GetEvent(const u32 event_id) const {
- return events_interface.events[event_id].readable;
+ return events_interface.events[event_id].event.readable;
}
std::shared_ptr<Kernel::WritableEvent> Module::GetEventWriteable(const u32 event_id) const {
- return events_interface.events[event_id].writable;
+ return events_interface.events[event_id].event.writable;
}
} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/nvdrv.h b/src/core/hle/service/nvdrv/nvdrv.h
index d7a1bef91..7654bb026 100644
--- a/src/core/hle/service/nvdrv/nvdrv.h
+++ b/src/core/hle/service/nvdrv/nvdrv.h
@@ -10,6 +10,7 @@
#include "common/common_types.h"
#include "core/hle/kernel/writable_event.h"
#include "core/hle/service/nvdrv/nvdata.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
#include "core/hle/service/service.h"
namespace Core {
@@ -22,15 +23,23 @@ class NVFlinger;
namespace Service::Nvidia {
+class SyncpointManager;
+
namespace Devices {
class nvdevice;
}
+/// Represents an Nvidia event
+struct NvEvent {
+ Kernel::EventPair event;
+ Fence fence{};
+};
+
struct EventInterface {
// Mask representing currently busy events
u64 events_mask{};
// Each kernel event associated to an NV event
- std::array<Kernel::EventPair, MaxNvEvents> events;
+ std::array<NvEvent, MaxNvEvents> events;
// The status of the current NVEvent
std::array<EventState, MaxNvEvents> status{};
// Tells if an NVEvent is registered or not
@@ -54,7 +63,7 @@ struct EventInterface {
}
mask = mask >> 1;
}
- return {};
+ return std::nullopt;
}
void SetEventStatus(const u32 event_id, EventState new_status) {
EventState old_status = status[event_id];
@@ -103,14 +112,23 @@ public:
return std::static_pointer_cast<T>(itr->second);
}
+ NvResult VerifyFD(DeviceFD fd) const;
+
/// Opens a device node and returns a file descriptor to it.
- u32 Open(const std::string& device_name);
+ DeviceFD Open(const std::string& device_name);
+
/// Sends an ioctl command to the specified file descriptor.
- u32 Ioctl(u32 fd, u32 command, const std::vector<u8>& input, const std::vector<u8>& input2,
- std::vector<u8>& output, std::vector<u8>& output2, IoctlCtrl& ctrl,
- IoctlVersion version);
+ NvResult Ioctl1(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output);
+
+ NvResult Ioctl2(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ const std::vector<u8>& inline_input, std::vector<u8>& output);
+
+ NvResult Ioctl3(DeviceFD fd, Ioctl command, const std::vector<u8>& input,
+ std::vector<u8>& output, std::vector<u8>& inline_output);
+
/// Closes a device file descriptor and returns operation success.
- ResultCode Close(u32 fd);
+ NvResult Close(DeviceFD fd);
void SignalSyncpt(const u32 syncpoint_id, const u32 value);
@@ -119,11 +137,14 @@ public:
std::shared_ptr<Kernel::WritableEvent> GetEventWriteable(u32 event_id) const;
private:
+ /// Manages syncpoints on the host
+ SyncpointManager syncpoint_manager;
+
/// Id to use for the next open file descriptor.
- u32 next_fd = 1;
+ DeviceFD next_fd = 1;
/// Mapping of file descriptors to the devices they reference.
- std::unordered_map<u32, std::shared_ptr<Devices::nvdevice>> open_files;
+ std::unordered_map<DeviceFD, std::shared_ptr<Devices::nvdevice>> open_files;
/// Mapping of device node names to their implementation.
std::unordered_map<std::string, std::shared_ptr<Devices::nvdevice>> devices;
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.cpp b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
new file mode 100644
index 000000000..0151a03b7
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.cpp
@@ -0,0 +1,39 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "core/hle/service/nvdrv/syncpoint_manager.h"
+#include "video_core/gpu.h"
+
+namespace Service::Nvidia {
+
+SyncpointManager::SyncpointManager(Tegra::GPU& gpu) : gpu{gpu} {}
+
+SyncpointManager::~SyncpointManager() = default;
+
+u32 SyncpointManager::RefreshSyncpoint(u32 syncpoint_id) {
+ syncpoints[syncpoint_id].min = gpu.GetSyncpointValue(syncpoint_id);
+ return GetSyncpointMin(syncpoint_id);
+}
+
+u32 SyncpointManager::AllocateSyncpoint() {
+ for (u32 syncpoint_id = 1; syncpoint_id < MaxSyncPoints; syncpoint_id++) {
+ if (!syncpoints[syncpoint_id].is_allocated) {
+ syncpoints[syncpoint_id].is_allocated = true;
+ return syncpoint_id;
+ }
+ }
+ UNREACHABLE_MSG("No more available syncpoints!");
+ return {};
+}
+
+u32 SyncpointManager::IncreaseSyncpoint(u32 syncpoint_id, u32 value) {
+ for (u32 index = 0; index < value; ++index) {
+ syncpoints[syncpoint_id].max.fetch_add(1, std::memory_order_relaxed);
+ }
+
+ return GetSyncpointMax(syncpoint_id);
+}
+
+} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvdrv/syncpoint_manager.h b/src/core/hle/service/nvdrv/syncpoint_manager.h
new file mode 100644
index 000000000..4168b6c7e
--- /dev/null
+++ b/src/core/hle/service/nvdrv/syncpoint_manager.h
@@ -0,0 +1,85 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <atomic>
+
+#include "common/common_types.h"
+#include "core/hle/service/nvdrv/nvdata.h"
+
+namespace Tegra {
+class GPU;
+}
+
+namespace Service::Nvidia {
+
+class SyncpointManager final {
+public:
+ explicit SyncpointManager(Tegra::GPU& gpu);
+ ~SyncpointManager();
+
+ /**
+ * Returns true if the specified syncpoint is expired for the given value.
+ * @param syncpoint_id Syncpoint ID to check.
+ * @param value Value to check against the specified syncpoint.
+ * @returns True if the specified syncpoint is expired for the given value, otherwise False.
+ */
+ bool IsSyncpointExpired(u32 syncpoint_id, u32 value) const {
+ return (GetSyncpointMax(syncpoint_id) - value) >= (GetSyncpointMin(syncpoint_id) - value);
+ }
+
+ /**
+ * Gets the lower bound for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to get the lower bound for.
+ * @returns The lower bound for the specified syncpoint.
+ */
+ u32 GetSyncpointMin(u32 syncpoint_id) const {
+ return syncpoints[syncpoint_id].min.load(std::memory_order_relaxed);
+ }
+
+ /**
+ * Gets the uper bound for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to get the upper bound for.
+ * @returns The upper bound for the specified syncpoint.
+ */
+ u32 GetSyncpointMax(u32 syncpoint_id) const {
+ return syncpoints[syncpoint_id].max.load(std::memory_order_relaxed);
+ }
+
+ /**
+ * Refreshes the minimum value for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to be refreshed.
+ * @returns The new syncpoint minimum value.
+ */
+ u32 RefreshSyncpoint(u32 syncpoint_id);
+
+ /**
+ * Allocates a new syncoint.
+ * @returns The syncpoint ID for the newly allocated syncpoint.
+ */
+ u32 AllocateSyncpoint();
+
+ /**
+ * Increases the maximum value for the specified syncpoint.
+ * @param syncpoint_id Syncpoint ID to be increased.
+ * @param value Value to increase the specified syncpoint by.
+ * @returns The new syncpoint maximum value.
+ */
+ u32 IncreaseSyncpoint(u32 syncpoint_id, u32 value);
+
+private:
+ struct Syncpoint {
+ std::atomic<u32> min;
+ std::atomic<u32> max;
+ std::atomic<bool> is_allocated;
+ };
+
+ std::array<Syncpoint, MaxSyncPoints> syncpoints{};
+
+ Tegra::GPU& gpu;
+};
+
+} // namespace Service::Nvidia
diff --git a/src/core/hle/service/nvflinger/buffer_queue.cpp b/src/core/hle/service/nvflinger/buffer_queue.cpp
index 637b310d7..b89a2d41b 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.cpp
+++ b/src/core/hle/service/nvflinger/buffer_queue.cpp
@@ -29,6 +29,10 @@ void BufferQueue::SetPreallocatedBuffer(u32 slot, const IGBPBuffer& igbp_buffer)
.slot = slot,
.status = Buffer::Status::Free,
.igbp_buffer = igbp_buffer,
+ .transform = {},
+ .crop_rect = {},
+ .swap_interval = 0,
+ .multi_fence = {},
});
buffer_wait_event.writable->Signal();
@@ -99,6 +103,20 @@ void BufferQueue::QueueBuffer(u32 slot, BufferTransformFlags transform,
queue_sequence.push_back(slot);
}
+void BufferQueue::CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence) {
+ const auto itr = std::find_if(queue.begin(), queue.end(),
+ [slot](const Buffer& buffer) { return buffer.slot == slot; });
+ ASSERT(itr != queue.end());
+ ASSERT(itr->status != Buffer::Status::Free);
+ itr->status = Buffer::Status::Free;
+ itr->multi_fence = multi_fence;
+ itr->swap_interval = 0;
+
+ free_buffers.push_back(slot);
+
+ buffer_wait_event.writable->Signal();
+}
+
std::optional<std::reference_wrapper<const BufferQueue::Buffer>> BufferQueue::AcquireBuffer() {
auto itr = queue.end();
// Iterate to find a queued buffer matching the requested slot.
diff --git a/src/core/hle/service/nvflinger/buffer_queue.h b/src/core/hle/service/nvflinger/buffer_queue.h
index 8a837e5aa..e7517c7e1 100644
--- a/src/core/hle/service/nvflinger/buffer_queue.h
+++ b/src/core/hle/service/nvflinger/buffer_queue.h
@@ -95,6 +95,7 @@ public:
void QueueBuffer(u32 slot, BufferTransformFlags transform,
const Common::Rectangle<int>& crop_rect, u32 swap_interval,
Service::Nvidia::MultiFence& multi_fence);
+ void CancelBuffer(u32 slot, const Service::Nvidia::MultiFence& multi_fence);
std::optional<std::reference_wrapper<const Buffer>> AcquireBuffer();
void ReleaseBuffer(u32 slot);
void Disconnect();
diff --git a/src/core/hle/service/nvflinger/nvflinger.cpp b/src/core/hle/service/nvflinger/nvflinger.cpp
index f644a460d..44aa2bdae 100644
--- a/src/core/hle/service/nvflinger/nvflinger.cpp
+++ b/src/core/hle/service/nvflinger/nvflinger.cpp
@@ -114,7 +114,7 @@ std::optional<u64> NVFlinger::OpenDisplay(std::string_view name) {
[&](const VI::Display& display) { return display.GetName() == name; });
if (itr == displays.end()) {
- return {};
+ return std::nullopt;
}
return itr->GetID();
@@ -124,7 +124,7 @@ std::optional<u64> NVFlinger::CreateLayer(u64 display_id) {
auto* const display = FindDisplay(display_id);
if (display == nullptr) {
- return {};
+ return std::nullopt;
}
const u64 layer_id = next_layer_id++;
@@ -144,7 +144,7 @@ std::optional<u32> NVFlinger::FindBufferQueueId(u64 display_id, u64 layer_id) co
const auto* const layer = FindLayer(display_id, layer_id);
if (layer == nullptr) {
- return {};
+ return std::nullopt;
}
return layer->GetBufferQueue().GetId();
@@ -242,6 +242,10 @@ void NVFlinger::Compose() {
const auto& igbp_buffer = buffer->get().igbp_buffer;
+ if (!system.IsPoweredOn()) {
+ return; // We are likely shutting down
+ }
+
auto& gpu = system.GPU();
const auto& multi_fence = buffer->get().multi_fence;
guard->unlock();
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 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/service/olsc/olsc.h"
+#include "core/hle/service/service.h"
+#include "core/hle/service/sm/sm.h"
+
+namespace Service::OLSC {
+
+class OLSC final : public ServiceFramework<OLSC> {
+public:
+ explicit OLSC() : ServiceFramework{"olsc:u"} {
+ // clang-format off
+ static const FunctionInfo functions[] = {
+ {0, &OLSC::Initialize, "Initialize"},
+ {10, nullptr, "VerifySaveDataBackupLicenseAsync"},
+ {13, nullptr, "GetSaveDataBackupSetting"},
+ {14, &OLSC::SetSaveDataBackupSettingEnabled, "SetSaveDataBackupSettingEnabled"},
+ {15, nullptr, "SetCustomData"},
+ {16, nullptr, "DeleteSaveDataBackupSetting"},
+ {18, nullptr, "GetSaveDataBackupInfoCache"},
+ {19, nullptr, "UpdateSaveDataBackupInfoCacheAsync"},
+ {22, nullptr, "DeleteSaveDataBackupAsync"},
+ {25, nullptr, "ListDownloadableSaveDataBackupInfoAsync"},
+ {26, nullptr, "DownloadSaveDataBackupAsync"},
+ {9010, nullptr, "VerifySaveDataBackupLicenseAsyncForDebug"},
+ {9013, nullptr, "GetSaveDataBackupSettingForDebug"},
+ {9014, nullptr, "SetSaveDataBackupSettingEnabledForDebug"},
+ {9015, nullptr, "SetCustomDataForDebug"},
+ {9016, nullptr, "DeleteSaveDataBackupSettingForDebug"},
+ {9018, nullptr, "GetSaveDataBackupInfoCacheForDebug"},
+ {9019, nullptr, "UpdateSaveDataBackupInfoCacheAsyncForDebug"},
+ {9022, nullptr, "DeleteSaveDataBackupAsyncForDebug"},
+ {9025, nullptr, "ListDownloadableSaveDataBackupInfoAsyncForDebug"},
+ {9026, nullptr, "DownloadSaveDataBackupAsyncForDebug"},
+ };
+ // clang-format on
+
+ RegisterHandlers(functions);
+ }
+
+private:
+ void Initialize(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_OLSC, "(STUBBED) called");
+
+ initialized = true;
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ void SetSaveDataBackupSettingEnabled(Kernel::HLERequestContext& ctx) {
+ LOG_WARNING(Service_OLSC, "(STUBBED) called");
+
+ IPC::ResponseBuilder rb{ctx, 2};
+ rb.Push(RESULT_SUCCESS);
+ }
+
+ bool initialized{};
+};
+
+void InstallInterfaces(SM::ServiceManager& service_manager) {
+ std::make_shared<OLSC>()->InstallAsService(service_manager);
+}
+
+} // 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 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+namespace Service::SM {
+class ServiceManager;
+}
+
+namespace Service::OLSC {
+
+/// Registers all SSL services with the specified service manager.
+void InstallInterfaces(SM::ServiceManager& service_manager);
+
+} // 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 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/kernel.h"
#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 @@
#include "common/hex_util.h"
#include "common/logging/log.h"
+#include "core/core.h"
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/process.h"
#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 fa5347af9..fbfda2d5b 100644
--- a/src/core/hle/service/service.cpp
+++ b/src/core/hle/service/service.cpp
@@ -51,6 +51,7 @@
#include "core/hle/service/ns/ns.h"
#include "core/hle/service/nvdrv/nvdrv.h"
#include "core/hle/service/nvflinger/nvflinger.h"
+#include "core/hle/service/olsc/olsc.h"
#include "core/hle/service/pcie/pcie.h"
#include "core/hle/service/pctl/module.h"
#include "core/hle/service/pcv/pcv.h"
@@ -89,8 +90,6 @@ namespace Service {
return function_string;
}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-
ServiceFrameworkBase::ServiceFrameworkBase(const char* service_name, u32 max_sessions,
InvokerFn* handler_invoker)
: service_name(service_name), max_sessions(max_sessions), handler_invoker(handler_invoker) {}
@@ -105,10 +104,9 @@ void ServiceFrameworkBase::InstallAsService(SM::ServiceManager& service_manager)
port_installed = true;
}
-void ServiceFrameworkBase::InstallAsNamedPort() {
+void ServiceFrameworkBase::InstallAsNamedPort(Kernel::KernelCore& kernel) {
ASSERT(!port_installed);
- auto& kernel = Core::System::GetInstance().Kernel();
auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
server_port->SetHleHandler(shared_from_this());
@@ -116,10 +114,9 @@ void ServiceFrameworkBase::InstallAsNamedPort() {
port_installed = true;
}
-std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort() {
+std::shared_ptr<Kernel::ClientPort> ServiceFrameworkBase::CreatePort(Kernel::KernelCore& kernel) {
ASSERT(!port_installed);
- auto& kernel = Core::System::GetInstance().Kernel();
auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, service_name);
auto port = MakeResult(std::move(server_port)).Unwrap();
@@ -191,9 +188,6 @@ ResultCode ServiceFrameworkBase::HandleSyncRequest(Kernel::HLERequestContext& co
return RESULT_SUCCESS;
}
-////////////////////////////////////////////////////////////////////////////////////////////////////
-// Module interface
-
/// Initialize ServiceManager
void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
// NVFlinger needs to be accessed by several services like Vi and AppletOE so we instantiate it
@@ -238,6 +232,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
NPNS::InstallInterfaces(*sm);
NS::InstallInterfaces(*sm, system);
Nvidia::InstallInterfaces(*sm, *nv_flinger, system);
+ OLSC::InstallInterfaces(*sm);
PCIe::InstallInterfaces(*sm);
PCTL::InstallInterfaces(*sm);
PCV::InstallInterfaces(*sm);
@@ -246,7 +241,7 @@ void Init(std::shared_ptr<SM::ServiceManager>& sm, Core::System& system) {
PSC::InstallInterfaces(*sm);
PSM::InstallInterfaces(*sm);
Set::InstallInterfaces(*sm);
- Sockets::InstallInterfaces(*sm);
+ Sockets::InstallInterfaces(*sm, system);
SPL::InstallInterfaces(*sm);
SSL::InstallInterfaces(*sm);
Time::InstallInterfaces(system);
diff --git a/src/core/hle/service/service.h b/src/core/hle/service/service.h
index 022d885b6..a01ef3353 100644
--- a/src/core/hle/service/service.h
+++ b/src/core/hle/service/service.h
@@ -63,9 +63,9 @@ public:
/// Creates a port pair and registers this service with the given ServiceManager.
void InstallAsService(SM::ServiceManager& service_manager);
/// Creates a port pair and registers it on the kernel's global port registry.
- void InstallAsNamedPort();
+ void InstallAsNamedPort(Kernel::KernelCore& kernel);
/// Creates and returns an unregistered port for the service.
- std::shared_ptr<Kernel::ClientPort> CreatePort();
+ std::shared_ptr<Kernel::ClientPort> CreatePort(Kernel::KernelCore& kernel);
void InvokeRequest(Kernel::HLERequestContext& ctx);
diff --git a/src/core/hle/service/set/set.cpp b/src/core/hle/service/set/set.cpp
index e64777668..ffbf90b00 100644
--- a/src/core/hle/service/set/set.cpp
+++ b/src/core/hle/service/set/set.cpp
@@ -202,6 +202,7 @@ SET::SET() : ServiceFramework("set") {
{8, &SET::GetQuestFlag, "GetQuestFlag"},
{9, &SET::GetKeyCodeMap2, "GetKeyCodeMap2"},
{10, nullptr, "GetFirmwareVersionForDebug"},
+ {11, nullptr, "GetDeviceNickName"},
};
// clang-format on
diff --git a/src/core/hle/service/set/set_sys.cpp b/src/core/hle/service/set/set_sys.cpp
index 8bd4c7e79..080b5743e 100644
--- a/src/core/hle/service/set/set_sys.cpp
+++ b/src/core/hle/service/set/set_sys.cpp
@@ -300,6 +300,8 @@ SET_SYS::SET_SYS() : ServiceFramework("set:sys") {
{198, nullptr, "SetButtonConfigRegisteredSettingsEmbedded"},
{199, nullptr, "GetButtonConfigRegisteredSettings"},
{200, nullptr, "SetButtonConfigRegisteredSettings"},
+ {201, nullptr, "GetFieldTestingFlag"},
+ {202, nullptr, "SetFieldTestingFlag"},
};
// clang-format on
diff --git a/src/core/hle/service/sm/sm.cpp b/src/core/hle/service/sm/sm.cpp
index d872de16c..9c1da361b 100644
--- a/src/core/hle/service/sm/sm.cpp
+++ b/src/core/hle/service/sm/sm.cpp
@@ -19,7 +19,7 @@ constexpr ResultCode ERR_ALREADY_REGISTERED(ErrorModule::SM, 4);
constexpr ResultCode ERR_INVALID_NAME(ErrorModule::SM, 6);
constexpr ResultCode ERR_SERVICE_NOT_REGISTERED(ErrorModule::SM, 7);
-ServiceManager::ServiceManager() = default;
+ServiceManager::ServiceManager(Kernel::KernelCore& kernel_) : kernel{kernel_} {}
ServiceManager::~ServiceManager() = default;
void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
@@ -27,11 +27,11 @@ void ServiceManager::InvokeControlRequest(Kernel::HLERequestContext& context) {
}
static ResultCode ValidateServiceName(const std::string& name) {
- if (name.size() <= 0 || name.size() > 8) {
+ if (name.empty() || name.size() > 8) {
LOG_ERROR(Service_SM, "Invalid service name! service={}", name);
return ERR_INVALID_NAME;
}
- if (name.find('\0') != std::string::npos) {
+ if (name.rfind('\0') != std::string::npos) {
LOG_ERROR(Service_SM, "A non null terminated service was passed");
return ERR_INVALID_NAME;
}
@@ -43,13 +43,13 @@ void ServiceManager::InstallInterfaces(std::shared_ptr<ServiceManager> self,
ASSERT(self->sm_interface.expired());
auto sm = std::make_shared<SM>(self, kernel);
- sm->InstallAsNamedPort();
+ sm->InstallAsNamedPort(kernel);
self->sm_interface = sm;
self->controller_interface = std::make_unique<Controller>();
}
-ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(
- std::string name, unsigned int max_sessions) {
+ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(std::string name,
+ u32 max_sessions) {
CASCADE_CODE(ValidateServiceName(name));
@@ -58,7 +58,6 @@ ResultVal<std::shared_ptr<Kernel::ServerPort>> ServiceManager::RegisterService(
return ERR_ALREADY_REGISTERED;
}
- auto& kernel = Core::System::GetInstance().Kernel();
auto [server_port, client_port] =
Kernel::ServerPort::CreatePortPair(kernel, max_sessions, name);
diff --git a/src/core/hle/service/sm/sm.h b/src/core/hle/service/sm/sm.h
index aabf166b7..6790c86f0 100644
--- a/src/core/hle/service/sm/sm.h
+++ b/src/core/hle/service/sm/sm.h
@@ -48,11 +48,11 @@ class ServiceManager {
public:
static void InstallInterfaces(std::shared_ptr<ServiceManager> self, Kernel::KernelCore& kernel);
- ServiceManager();
+ explicit ServiceManager(Kernel::KernelCore& kernel_);
~ServiceManager();
ResultVal<std::shared_ptr<Kernel::ServerPort>> RegisterService(std::string name,
- unsigned int max_sessions);
+ u32 max_sessions);
ResultCode UnregisterService(const std::string& name);
ResultVal<std::shared_ptr<Kernel::ClientPort>> GetServicePort(const std::string& name);
ResultVal<std::shared_ptr<Kernel::ClientSession>> ConnectToService(const std::string& name);
@@ -79,6 +79,9 @@ private:
/// Map of registered services, retrieved using GetServicePort or ConnectToService.
std::unordered_map<std::string, std::shared_ptr<Kernel::ClientPort>> registered_services;
+
+ /// Kernel context
+ Kernel::KernelCore& kernel;
};
} // namespace Service::SM
diff --git a/src/core/hle/service/sockets/blocking_worker.h b/src/core/hle/service/sockets/blocking_worker.h
new file mode 100644
index 000000000..2d53e52b6
--- /dev/null
+++ b/src/core/hle/service/sockets/blocking_worker.h
@@ -0,0 +1,161 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <memory>
+#include <string>
+#include <string_view>
+#include <thread>
+#include <variant>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include "common/assert.h"
+#include "common/microprofile.h"
+#include "common/thread.h"
+#include "core/core.h"
+#include "core/hle/kernel/hle_ipc.h"
+#include "core/hle/kernel/kernel.h"
+#include "core/hle/kernel/thread.h"
+#include "core/hle/kernel/writable_event.h"
+
+namespace Service::Sockets {
+
+/**
+ * Worker abstraction to execute blocking calls on host without blocking the guest thread
+ *
+ * @tparam Service Service where the work is executed
+ * @tparam Types Types of work to execute
+ */
+template <class Service, class... Types>
+class BlockingWorker {
+ using This = BlockingWorker<Service, Types...>;
+ using WorkVariant = std::variant<std::monostate, Types...>;
+
+public:
+ /// Create a new worker
+ static std::unique_ptr<This> Create(Core::System& system, Service* service,
+ std::string_view name) {
+ return std::unique_ptr<This>(new This(system, service, name));
+ }
+
+ ~BlockingWorker() {
+ while (!is_available.load(std::memory_order_relaxed)) {
+ // Busy wait until work is finished
+ std::this_thread::yield();
+ }
+ // Monostate means to exit the thread
+ work = std::monostate{};
+ work_event.Set();
+ thread.join();
+ }
+
+ /**
+ * Try to capture the worker to send work after a success
+ * @returns True when the worker has been successfully captured
+ */
+ bool TryCapture() {
+ bool expected = true;
+ return is_available.compare_exchange_weak(expected, false, std::memory_order_relaxed,
+ std::memory_order_relaxed);
+ }
+
+ /**
+ * Send work to this worker abstraction
+ * @see TryCapture must be called before attempting to call this function
+ */
+ template <class Work>
+ void SendWork(Work new_work) {
+ ASSERT_MSG(!is_available, "Trying to send work on a worker that's not captured");
+ work = std::move(new_work);
+ work_event.Set();
+ }
+
+ /// Generate a callback for @see SleepClientThread
+ template <class Work>
+ auto Callback() {
+ return [this](std::shared_ptr<Kernel::Thread>, Kernel::HLERequestContext& ctx,
+ Kernel::ThreadWakeupReason reason) {
+ ASSERT(reason == Kernel::ThreadWakeupReason::Signal);
+ std::get<Work>(work).Response(ctx);
+ is_available.store(true);
+ };
+ }
+
+ /// Get kernel event that will be signalled by the worker when the host operation finishes
+ std::shared_ptr<Kernel::WritableEvent> KernelEvent() const {
+ return kernel_event;
+ }
+
+private:
+ explicit BlockingWorker(Core::System& system, Service* service, std::string_view name) {
+ auto pair = Kernel::WritableEvent::CreateEventPair(system.Kernel(), std::string(name));
+ kernel_event = std::move(pair.writable);
+ thread = std::thread([this, &system, service, name] { Run(system, service, name); });
+ }
+
+ void Run(Core::System& system, Service* service, std::string_view name) {
+ system.RegisterHostThread();
+
+ const std::string thread_name = fmt::format("yuzu:{}", name);
+ MicroProfileOnThreadCreate(thread_name.c_str());
+ Common::SetCurrentThreadName(thread_name.c_str());
+
+ bool keep_running = true;
+ while (keep_running) {
+ work_event.Wait();
+
+ const auto visit_fn = [service, &keep_running]<typename T>(T&& w) {
+ if constexpr (std::is_same_v<std::decay_t<T>, std::monostate>) {
+ keep_running = false;
+ } else {
+ w.Execute(service);
+ }
+ };
+ std::visit(visit_fn, work);
+
+ kernel_event->Signal();
+ }
+ }
+
+ std::thread thread;
+ WorkVariant work;
+ Common::Event work_event;
+ std::shared_ptr<Kernel::WritableEvent> kernel_event;
+ std::atomic_bool is_available{true};
+};
+
+template <class Service, class... Types>
+class BlockingWorkerPool {
+ using Worker = BlockingWorker<Service, Types...>;
+
+public:
+ explicit BlockingWorkerPool(Core::System& system_, Service* service_)
+ : system{system_}, service{service_} {}
+
+ /// Returns a captured worker thread, creating new ones if necessary
+ Worker* CaptureWorker() {
+ for (auto& worker : workers) {
+ if (worker->TryCapture()) {
+ return worker.get();
+ }
+ }
+ auto new_worker = Worker::Create(system, service, fmt::format("BSD:{}", workers.size()));
+ [[maybe_unused]] const bool success = new_worker->TryCapture();
+ ASSERT(success);
+
+ return workers.emplace_back(std::move(new_worker)).get();
+ }
+
+private:
+ Core::System& system;
+ Service* const service;
+
+ std::vector<std::unique_ptr<Worker>> workers;
+};
+
+} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/bsd.cpp b/src/core/hle/service/sockets/bsd.cpp
index 8d4952c0e..a74be9370 100644
--- a/src/core/hle/service/sockets/bsd.cpp
+++ b/src/core/hle/service/sockets/bsd.cpp
@@ -2,18 +2,138 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <array>
+#include <memory>
+#include <string>
+#include <utility>
+#include <vector>
+
+#include <fmt/format.h>
+
+#include "common/microprofile.h"
+#include "common/thread.h"
#include "core/hle/ipc_helpers.h"
+#include "core/hle/kernel/thread.h"
#include "core/hle/service/sockets/bsd.h"
+#include "core/hle/service/sockets/sockets_translate.h"
+#include "core/network/network.h"
+#include "core/network/sockets.h"
namespace Service::Sockets {
+namespace {
+
+bool IsConnectionBased(Type type) {
+ switch (type) {
+ case Type::STREAM:
+ return true;
+ case Type::DGRAM:
+ return false;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
+ return false;
+ }
+}
+
+} // Anonymous namespace
+
+void BSD::PollWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->PollImpl(write_buffer, read_buffer, nfds, timeout);
+}
+
+void BSD::PollWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::AcceptWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->AcceptImpl(fd, write_buffer);
+}
+
+void BSD::AcceptWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(write_buffer.size()));
+}
+
+void BSD::ConnectWork::Execute(BSD* bsd) {
+ bsd_errno = bsd->ConnectImpl(fd, addr);
+}
+
+void BSD::ConnectWork::Response(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::RecvWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->RecvImpl(fd, flags, message);
+}
+
+void BSD::RecvWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(message);
+
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::RecvFromWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->RecvFromImpl(fd, flags, message, addr);
+}
+
+void BSD::RecvFromWork::Response(Kernel::HLERequestContext& ctx) {
+ ctx.WriteBuffer(message, 0);
+ if (!addr.empty()) {
+ ctx.WriteBuffer(addr, 1);
+ }
+
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(addr.size()));
+}
+
+void BSD::SendWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->SendImpl(fd, flags, message);
+}
+
+void BSD::SendWork::Response(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
+void BSD::SendToWork::Execute(BSD* bsd) {
+ std::tie(ret, bsd_errno) = bsd->SendToImpl(fd, flags, message, addr);
+}
+
+void BSD::SendToWork::Response(Kernel::HLERequestContext& ctx) {
+ IPC::ResponseBuilder rb{ctx, 4};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
+}
+
void BSD::RegisterClient(Kernel::HLERequestContext& ctx) {
LOG_WARNING(Service, "(STUBBED) called");
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(0); // bsd errno
}
void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
@@ -26,20 +146,19 @@ void BSD::StartMonitoring(Kernel::HLERequestContext& ctx) {
void BSD::Socket(Kernel::HLERequestContext& ctx) {
IPC::RequestParser rp{ctx};
+ const u32 domain = rp.Pop<u32>();
+ const u32 type = rp.Pop<u32>();
+ const u32 protocol = rp.Pop<u32>();
- u32 domain = rp.Pop<u32>();
- u32 type = rp.Pop<u32>();
- u32 protocol = rp.Pop<u32>();
-
- LOG_WARNING(Service, "(STUBBED) called domain={} type={} protocol={}", domain, type, protocol);
+ LOG_DEBUG(Service, "called. domain={} type={} protocol={}", domain, type, protocol);
- u32 fd = next_fd++;
+ const auto [fd, bsd_errno] = SocketImpl(static_cast<Domain>(domain), static_cast<Type>(type),
+ static_cast<Protocol>(protocol));
IPC::ResponseBuilder rb{ctx, 4};
-
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(fd);
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(fd);
+ rb.PushEnum(bsd_errno);
}
void BSD::Select(Kernel::HLERequestContext& ctx) {
@@ -52,67 +171,664 @@ void BSD::Select(Kernel::HLERequestContext& ctx) {
rb.Push<u32>(0); // bsd errno
}
+void BSD::Poll(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 nfds = rp.Pop<s32>();
+ const s32 timeout = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. nfds={} timeout={}", nfds, timeout);
+
+ ExecuteWork(ctx, "BSD:Poll", timeout != 0,
+ PollWork{
+ .nfds = nfds,
+ .timeout = timeout,
+ .read_buffer = ctx.ReadBuffer(),
+ .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
+ });
+}
+
+void BSD::Accept(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+
+ ExecuteWork(ctx, "BSD:Accept", IsBlockingSocket(fd),
+ AcceptWork{
+ .fd = fd,
+ .write_buffer = std::vector<u8>(ctx.GetWriteBufferSize()),
+ });
+}
+
void BSD::Bind(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
- IPC::ResponseBuilder rb{ctx, 4};
+ LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ BuildErrnoResponse(ctx, BindImpl(fd, ctx.ReadBuffer()));
}
void BSD::Connect(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
- IPC::ResponseBuilder rb{ctx, 4};
+ LOG_DEBUG(Service, "called. fd={} addrlen={}", fd, ctx.GetReadBufferSize());
+
+ ExecuteWork(ctx, "BSD:Connect", IsBlockingSocket(fd),
+ ConnectWork{
+ .fd = fd,
+ .addr = ctx.ReadBuffer(),
+ });
+}
+
+void BSD::GetPeerName(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+ std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
+ const Errno bsd_errno = GetPeerNameImpl(fd, write_buffer);
+
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 5};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(write_buffer.size()));
+}
+
+void BSD::GetSockName(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+
+ std::vector<u8> write_buffer(ctx.GetWriteBufferSize());
+ const Errno bsd_errno = GetSockNameImpl(fd, write_buffer);
+
+ ctx.WriteBuffer(write_buffer);
+
+ IPC::ResponseBuilder rb{ctx, 5};
+ rb.Push(RESULT_SUCCESS);
+ rb.Push<s32>(bsd_errno != Errno::SUCCESS ? -1 : 0);
+ rb.PushEnum(bsd_errno);
+ rb.Push<u32>(static_cast<u32>(write_buffer.size()));
}
void BSD::Listen(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+ const s32 backlog = rp.Pop<s32>();
- IPC::ResponseBuilder rb{ctx, 4};
+ LOG_DEBUG(Service, "called. fd={} backlog={}", fd, backlog);
+
+ BuildErrnoResponse(ctx, ListenImpl(fd, backlog));
+}
+
+void BSD::Fcntl(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+ const s32 cmd = rp.Pop<s32>();
+ const s32 arg = rp.Pop<s32>();
+ LOG_DEBUG(Service, "called. fd={} cmd={} arg={}", fd, cmd, arg);
+
+ const auto [ret, bsd_errno] = FcntlImpl(fd, static_cast<FcntlCmd>(cmd), arg);
+
+ IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(ret);
+ rb.PushEnum(bsd_errno);
}
void BSD::SetSockOpt(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
- IPC::ResponseBuilder rb{ctx, 4};
+ const s32 fd = rp.Pop<s32>();
+ const u32 level = rp.Pop<u32>();
+ const OptName optname = static_cast<OptName>(rp.Pop<u32>());
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ const std::vector<u8> buffer = ctx.ReadBuffer();
+ const u8* optval = buffer.empty() ? nullptr : buffer.data();
+ size_t optlen = buffer.size();
+
+ std::array<u64, 2> values;
+ if ((optname == OptName::SNDTIMEO || optname == OptName::RCVTIMEO) && buffer.size() == 8) {
+ std::memcpy(values.data(), buffer.data(), sizeof(values));
+ optlen = sizeof(values);
+ optval = reinterpret_cast<const u8*>(values.data());
+ }
+
+ LOG_DEBUG(Service, "called. fd={} level={} optname=0x{:x} optlen={}", fd, level,
+ static_cast<u32>(optname), optlen);
+
+ BuildErrnoResponse(ctx, SetSockOptImpl(fd, level, optname, optlen, optval));
+}
+
+void BSD::Shutdown(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const s32 how = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={} how={}", fd, how);
+
+ BuildErrnoResponse(ctx, ShutdownImpl(fd, how));
+}
+
+void BSD::Recv(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetWriteBufferSize());
+
+ ExecuteWork(ctx, "BSD:Recv", IsBlockingSocket(fd),
+ RecvWork{
+ .fd = fd,
+ .flags = flags,
+ .message = std::vector<u8>(ctx.GetWriteBufferSize()),
+ });
+}
+
+void BSD::RecvFrom(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={} addrlen={}", fd, flags,
+ ctx.GetWriteBufferSize(0), ctx.GetWriteBufferSize(1));
+
+ ExecuteWork(ctx, "BSD:RecvFrom", IsBlockingSocket(fd),
+ RecvFromWork{
+ .fd = fd,
+ .flags = flags,
+ .message = std::vector<u8>(ctx.GetWriteBufferSize(0)),
+ .addr = std::vector<u8>(ctx.GetWriteBufferSize(1)),
+ });
+}
+
+void BSD::Send(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{:x} len={}", fd, flags, ctx.GetReadBufferSize());
+
+ ExecuteWork(ctx, "BSD:Send", IsBlockingSocket(fd),
+ SendWork{
+ .fd = fd,
+ .flags = flags,
+ .message = ctx.ReadBuffer(),
+ });
}
void BSD::SendTo(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+ const u32 flags = rp.Pop<u32>();
+
+ LOG_DEBUG(Service, "called. fd={} flags=0x{} len={} addrlen={}", fd, flags,
+ ctx.GetReadBufferSize(0), ctx.GetReadBufferSize(1));
+
+ ExecuteWork(ctx, "BSD:SendTo", IsBlockingSocket(fd),
+ SendToWork{
+ .fd = fd,
+ .flags = flags,
+ .message = ctx.ReadBuffer(0),
+ .addr = ctx.ReadBuffer(1),
+ });
+}
- IPC::ResponseBuilder rb{ctx, 4};
+void BSD::Write(Kernel::HLERequestContext& ctx) {
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
- rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ LOG_DEBUG(Service, "called. fd={} len={}", fd, ctx.GetReadBufferSize());
+
+ ExecuteWork(ctx, "BSD:Write", IsBlockingSocket(fd),
+ SendWork{
+ .fd = fd,
+ .flags = 0,
+ .message = ctx.ReadBuffer(),
+ });
}
void BSD::Close(Kernel::HLERequestContext& ctx) {
- LOG_WARNING(Service, "(STUBBED) called");
+ IPC::RequestParser rp{ctx};
+ const s32 fd = rp.Pop<s32>();
+
+ LOG_DEBUG(Service, "called. fd={}", fd);
+
+ BuildErrnoResponse(ctx, CloseImpl(fd));
+}
+
+template <typename Work>
+void BSD::ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
+ bool is_blocking, Work work) {
+ if (!is_blocking) {
+ work.Execute(this);
+ work.Response(ctx);
+ return;
+ }
+
+ // Signal a dummy response to make IPC validation happy
+ // This will be overwritten by the SleepClientThread callback
+ work.Response(ctx);
+
+ auto worker = worker_pool.CaptureWorker();
+
+ ctx.SleepClientThread(std::string(sleep_reason), std::numeric_limits<u64>::max(),
+ worker->Callback<Work>(), worker->KernelEvent());
+
+ worker->SendWork(std::move(work));
+}
+
+std::pair<s32, Errno> BSD::SocketImpl(Domain domain, Type type, Protocol protocol) {
+ if (type == Type::SEQPACKET) {
+ UNIMPLEMENTED_MSG("SOCK_SEQPACKET errno management");
+ } else if (type == Type::RAW && (domain != Domain::INET || protocol != Protocol::ICMP)) {
+ UNIMPLEMENTED_MSG("SOCK_RAW errno management");
+ }
+
+ [[maybe_unused]] const bool unk_flag = (static_cast<u32>(type) & 0x20000000) != 0;
+ UNIMPLEMENTED_IF_MSG(unk_flag, "Unknown flag in type");
+ type = static_cast<Type>(static_cast<u32>(type) & ~0x20000000);
+
+ const s32 fd = FindFreeFileDescriptorHandle();
+ if (fd < 0) {
+ LOG_ERROR(Service, "No more file descriptors available");
+ return {-1, Errno::MFILE};
+ }
+
+ FileDescriptor& descriptor = file_descriptors[fd].emplace();
+ // ENONMEM might be thrown here
+
+ LOG_INFO(Service, "New socket fd={}", fd);
+
+ descriptor.socket = std::make_unique<Network::Socket>();
+ descriptor.socket->Initialize(Translate(domain), Translate(type), Translate(type, protocol));
+ descriptor.is_connection_based = IsConnectionBased(type);
+
+ return {fd, Errno::SUCCESS};
+}
+std::pair<s32, Errno> BSD::PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
+ s32 nfds, s32 timeout) {
+ if (write_buffer.size() < nfds * sizeof(PollFD)) {
+ return {-1, Errno::INVAL};
+ }
+
+ if (nfds == 0) {
+ // When no entries are provided, -1 is returned with errno zero
+ return {-1, Errno::SUCCESS};
+ }
+
+ const size_t length = std::min(read_buffer.size(), write_buffer.size());
+ std::vector<PollFD> fds(nfds);
+ std::memcpy(fds.data(), read_buffer.data(), length);
+
+ if (timeout >= 0) {
+ const s64 seconds = timeout / 1000;
+ const u64 nanoseconds = 1'000'000 * (static_cast<u64>(timeout) % 1000);
+
+ if (seconds < 0) {
+ return {-1, Errno::INVAL};
+ }
+ if (nanoseconds > 999'999'999) {
+ return {-1, Errno::INVAL};
+ }
+ } else if (timeout != -1) {
+ return {-1, Errno::INVAL};
+ }
+
+ for (PollFD& pollfd : fds) {
+ ASSERT(pollfd.revents == 0);
+
+ if (pollfd.fd > static_cast<s32>(MAX_FD) || pollfd.fd < 0) {
+ LOG_ERROR(Service, "File descriptor handle={} is invalid", pollfd.fd);
+ pollfd.revents = 0;
+ return {0, Errno::SUCCESS};
+ }
+
+ const std::optional<FileDescriptor>& descriptor = file_descriptors[pollfd.fd];
+ if (!descriptor) {
+ LOG_ERROR(Service, "File descriptor handle={} is not allocated", pollfd.fd);
+ pollfd.revents = POLL_NVAL;
+ return {0, Errno::SUCCESS};
+ }
+ }
+
+ std::vector<Network::PollFD> host_pollfds(fds.size());
+ std::transform(fds.begin(), fds.end(), host_pollfds.begin(), [this](PollFD pollfd) {
+ Network::PollFD result;
+ result.socket = file_descriptors[pollfd.fd]->socket.get();
+ result.events = TranslatePollEventsToHost(pollfd.events);
+ result.revents = 0;
+ return result;
+ });
+
+ const auto result = Network::Poll(host_pollfds, timeout);
+
+ const size_t num = host_pollfds.size();
+ for (size_t i = 0; i < num; ++i) {
+ fds[i].revents = TranslatePollEventsToGuest(host_pollfds[i].revents);
+ }
+ std::memcpy(write_buffer.data(), fds.data(), length);
+
+ return Translate(result);
+}
+
+std::pair<s32, Errno> BSD::AcceptImpl(s32 fd, std::vector<u8>& write_buffer) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ const s32 new_fd = FindFreeFileDescriptorHandle();
+ if (new_fd < 0) {
+ LOG_ERROR(Service, "No more file descriptors available");
+ return {-1, Errno::MFILE};
+ }
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+ auto [result, bsd_errno] = descriptor.socket->Accept();
+ if (bsd_errno != Network::Errno::SUCCESS) {
+ return {-1, Translate(bsd_errno)};
+ }
+
+ FileDescriptor& new_descriptor = file_descriptors[new_fd].emplace();
+ new_descriptor.socket = std::move(result.socket);
+ new_descriptor.is_connection_based = descriptor.is_connection_based;
+
+ ASSERT(write_buffer.size() == sizeof(SockAddrIn));
+ const SockAddrIn guest_addr_in = Translate(result.sockaddr_in);
+ std::memcpy(write_buffer.data(), &guest_addr_in, sizeof(guest_addr_in));
+
+ return {new_fd, Errno::SUCCESS};
+}
+
+Errno BSD::BindImpl(s32 fd, const std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+ ASSERT(addr.size() == sizeof(SockAddrIn));
+ SockAddrIn addr_in;
+ std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
+
+ return Translate(file_descriptors[fd]->socket->Bind(Translate(addr_in)));
+}
+
+Errno BSD::ConnectImpl(s32 fd, const std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ UNIMPLEMENTED_IF(addr.size() != sizeof(SockAddrIn));
+ SockAddrIn addr_in;
+ std::memcpy(&addr_in, addr.data(), sizeof(addr_in));
+
+ return Translate(file_descriptors[fd]->socket->Connect(Translate(addr_in)));
+}
+
+Errno BSD::GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetPeerName();
+ if (bsd_errno != Network::Errno::SUCCESS) {
+ return Translate(bsd_errno);
+ }
+ const SockAddrIn guest_addrin = Translate(addr_in);
+
+ ASSERT(write_buffer.size() == sizeof(guest_addrin));
+ std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
+ return Translate(bsd_errno);
+}
+
+Errno BSD::GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ const auto [addr_in, bsd_errno] = file_descriptors[fd]->socket->GetSockName();
+ if (bsd_errno != Network::Errno::SUCCESS) {
+ return Translate(bsd_errno);
+ }
+ const SockAddrIn guest_addrin = Translate(addr_in);
+
+ ASSERT(write_buffer.size() == sizeof(guest_addrin));
+ std::memcpy(write_buffer.data(), &guest_addrin, sizeof(guest_addrin));
+ return Translate(bsd_errno);
+}
+
+Errno BSD::ListenImpl(s32 fd, s32 backlog) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+ return Translate(file_descriptors[fd]->socket->Listen(backlog));
+}
+
+std::pair<s32, Errno> BSD::FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+
+ switch (cmd) {
+ case FcntlCmd::GETFL:
+ ASSERT(arg == 0);
+ return {descriptor.flags, Errno::SUCCESS};
+ case FcntlCmd::SETFL: {
+ const bool enable = (arg & FLAG_O_NONBLOCK) != 0;
+ const Errno bsd_errno = Translate(descriptor.socket->SetNonBlock(enable));
+ if (bsd_errno != Errno::SUCCESS) {
+ return {-1, bsd_errno};
+ }
+ descriptor.flags = arg;
+ return {0, Errno::SUCCESS};
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented cmd={}", static_cast<int>(cmd));
+ return {-1, Errno::SUCCESS};
+ }
+}
+
+Errno BSD::SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval) {
+ UNIMPLEMENTED_IF(level != 0xffff); // SOL_SOCKET
+
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ Network::Socket* const socket = file_descriptors[fd]->socket.get();
+
+ if (optname == OptName::LINGER) {
+ ASSERT(optlen == sizeof(Linger));
+ Linger linger;
+ std::memcpy(&linger, optval, sizeof(linger));
+ ASSERT(linger.onoff == 0 || linger.onoff == 1);
+
+ return Translate(socket->SetLinger(linger.onoff != 0, linger.linger));
+ }
+
+ ASSERT(optlen == sizeof(u32));
+ u32 value;
+ std::memcpy(&value, optval, sizeof(value));
+
+ switch (optname) {
+ case OptName::REUSEADDR:
+ ASSERT(value == 0 || value == 1);
+ return Translate(socket->SetReuseAddr(value != 0));
+ case OptName::BROADCAST:
+ ASSERT(value == 0 || value == 1);
+ return Translate(socket->SetBroadcast(value != 0));
+ case OptName::SNDBUF:
+ return Translate(socket->SetSndBuf(value));
+ case OptName::RCVBUF:
+ return Translate(socket->SetRcvBuf(value));
+ case OptName::SNDTIMEO:
+ return Translate(socket->SetSndTimeo(value));
+ case OptName::RCVTIMEO:
+ return Translate(socket->SetRcvTimeo(value));
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented optname={}", static_cast<int>(optname));
+ return Errno::SUCCESS;
+ }
+}
+
+Errno BSD::ShutdownImpl(s32 fd, s32 how) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+ const Network::ShutdownHow host_how = Translate(static_cast<ShutdownHow>(how));
+ return Translate(file_descriptors[fd]->socket->Shutdown(host_how));
+}
+
+std::pair<s32, Errno> BSD::RecvImpl(s32 fd, u32 flags, std::vector<u8>& message) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+ return Translate(file_descriptors[fd]->socket->Recv(flags, message));
+}
+
+std::pair<s32, Errno> BSD::RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
+ std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ FileDescriptor& descriptor = *file_descriptors[fd];
+
+ Network::SockAddrIn addr_in{};
+ Network::SockAddrIn* p_addr_in = nullptr;
+ if (descriptor.is_connection_based) {
+ // Connection based file descriptors (e.g. TCP) zero addr
+ addr.clear();
+ } else {
+ p_addr_in = &addr_in;
+ }
+
+ // Apply flags
+ if ((flags & FLAG_MSG_DONTWAIT) != 0) {
+ flags &= ~FLAG_MSG_DONTWAIT;
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(true);
+ }
+ }
+
+ const auto [ret, bsd_errno] = Translate(descriptor.socket->RecvFrom(flags, message, p_addr_in));
+
+ // Restore original state
+ if ((descriptor.flags & FLAG_O_NONBLOCK) == 0) {
+ descriptor.socket->SetNonBlock(false);
+ }
+
+ if (p_addr_in) {
+ if (ret < 0) {
+ addr.clear();
+ } else {
+ ASSERT(addr.size() == sizeof(SockAddrIn));
+ const SockAddrIn result = Translate(addr_in);
+ std::memcpy(addr.data(), &result, sizeof(result));
+ }
+ }
+
+ return {ret, bsd_errno};
+}
+
+std::pair<s32, Errno> BSD::SendImpl(s32 fd, u32 flags, const std::vector<u8>& message) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+ return Translate(file_descriptors[fd]->socket->Send(message, flags));
+}
+
+std::pair<s32, Errno> BSD::SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
+ const std::vector<u8>& addr) {
+ if (!IsFileDescriptorValid(fd)) {
+ return {-1, Errno::BADF};
+ }
+
+ Network::SockAddrIn addr_in;
+ Network::SockAddrIn* p_addr_in = nullptr;
+ if (!addr.empty()) {
+ ASSERT(addr.size() == sizeof(SockAddrIn));
+ SockAddrIn guest_addr_in;
+ std::memcpy(&guest_addr_in, addr.data(), sizeof(guest_addr_in));
+ addr_in = Translate(guest_addr_in);
+ p_addr_in = &addr_in;
+ }
+
+ return Translate(file_descriptors[fd]->socket->SendTo(flags, message, p_addr_in));
+}
+
+Errno BSD::CloseImpl(s32 fd) {
+ if (!IsFileDescriptorValid(fd)) {
+ return Errno::BADF;
+ }
+
+ const Errno bsd_errno = Translate(file_descriptors[fd]->socket->Close());
+ if (bsd_errno != Errno::SUCCESS) {
+ return bsd_errno;
+ }
+
+ LOG_INFO(Service, "Close socket fd={}", fd);
+
+ file_descriptors[fd].reset();
+ return bsd_errno;
+}
+
+s32 BSD::FindFreeFileDescriptorHandle() noexcept {
+ for (s32 fd = 0; fd < static_cast<s32>(file_descriptors.size()); ++fd) {
+ if (!file_descriptors[fd]) {
+ return fd;
+ }
+ }
+ return -1;
+}
+
+bool BSD::IsFileDescriptorValid(s32 fd) const noexcept {
+ if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
+ LOG_ERROR(Service, "Invalid file descriptor handle={}", fd);
+ return false;
+ }
+ if (!file_descriptors[fd]) {
+ LOG_ERROR(Service, "File descriptor handle={} is not allocated", fd);
+ return false;
+ }
+ return true;
+}
+
+bool BSD::IsBlockingSocket(s32 fd) const noexcept {
+ // Inform invalid sockets as non-blocking
+ // This way we avoid using a worker thread as it will fail without blocking host
+ if (fd > static_cast<s32>(MAX_FD) || fd < 0) {
+ return false;
+ }
+ if (!file_descriptors[fd]) {
+ return false;
+ }
+ return (file_descriptors[fd]->flags & FLAG_O_NONBLOCK) != 0;
+}
+
+void BSD::BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept {
IPC::ResponseBuilder rb{ctx, 4};
rb.Push(RESULT_SUCCESS);
- rb.Push<u32>(0); // ret
- rb.Push<u32>(0); // bsd errno
+ rb.Push<s32>(bsd_errno == Errno::SUCCESS ? 0 : -1);
+ rb.PushEnum(bsd_errno);
}
-BSD::BSD(const char* name) : ServiceFramework(name) {
+BSD::BSD(Core::System& system, const char* name)
+ : ServiceFramework(name), worker_pool{system, this} {
// clang-format off
static const FunctionInfo functions[] = {
{0, &BSD::RegisterClient, "RegisterClient"},
@@ -121,25 +837,25 @@ BSD::BSD(const char* name) : ServiceFramework(name) {
{3, nullptr, "SocketExempt"},
{4, nullptr, "Open"},
{5, &BSD::Select, "Select"},
- {6, nullptr, "Poll"},
+ {6, &BSD::Poll, "Poll"},
{7, nullptr, "Sysctl"},
- {8, nullptr, "Recv"},
- {9, nullptr, "RecvFrom"},
- {10, nullptr, "Send"},
+ {8, &BSD::Recv, "Recv"},
+ {9, &BSD::RecvFrom, "RecvFrom"},
+ {10, &BSD::Send, "Send"},
{11, &BSD::SendTo, "SendTo"},
- {12, nullptr, "Accept"},
+ {12, &BSD::Accept, "Accept"},
{13, &BSD::Bind, "Bind"},
{14, &BSD::Connect, "Connect"},
- {15, nullptr, "GetPeerName"},
- {16, nullptr, "GetSockName"},
+ {15, &BSD::GetPeerName, "GetPeerName"},
+ {16, &BSD::GetSockName, "GetSockName"},
{17, nullptr, "GetSockOpt"},
{18, &BSD::Listen, "Listen"},
{19, nullptr, "Ioctl"},
- {20, nullptr, "Fcntl"},
+ {20, &BSD::Fcntl, "Fcntl"},
{21, &BSD::SetSockOpt, "SetSockOpt"},
- {22, nullptr, "Shutdown"},
+ {22, &BSD::Shutdown, "Shutdown"},
{23, nullptr, "ShutdownAllSockets"},
- {24, nullptr, "Write"},
+ {24, &BSD::Write, "Write"},
{25, nullptr, "Read"},
{26, &BSD::Close, "Close"},
{27, nullptr, "DuplicateSocket"},
diff --git a/src/core/hle/service/sockets/bsd.h b/src/core/hle/service/sockets/bsd.h
index 3098e3baf..357531951 100644
--- a/src/core/hle/service/sockets/bsd.h
+++ b/src/core/hle/service/sockets/bsd.h
@@ -4,30 +4,174 @@
#pragma once
+#include <memory>
+#include <string_view>
+#include <vector>
+
+#include "common/common_types.h"
#include "core/hle/kernel/hle_ipc.h"
#include "core/hle/service/service.h"
+#include "core/hle/service/sockets/blocking_worker.h"
+#include "core/hle/service/sockets/sockets.h"
+
+namespace Core {
+class System;
+}
+
+namespace Network {
+class Socket;
+}
namespace Service::Sockets {
class BSD final : public ServiceFramework<BSD> {
public:
- explicit BSD(const char* name);
+ explicit BSD(Core::System& system, const char* name);
~BSD() override;
private:
+ /// Maximum number of file descriptors
+ static constexpr size_t MAX_FD = 128;
+
+ struct FileDescriptor {
+ std::unique_ptr<Network::Socket> socket;
+ s32 flags = 0;
+ bool is_connection_based = false;
+ };
+
+ struct PollWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 nfds;
+ s32 timeout;
+ std::vector<u8> read_buffer;
+ std::vector<u8> write_buffer;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct AcceptWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ std::vector<u8> write_buffer;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct ConnectWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ std::vector<u8> addr;
+ Errno bsd_errno{};
+ };
+
+ struct RecvWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct RecvFromWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ std::vector<u8> addr;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct SendWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
+ struct SendToWork {
+ void Execute(BSD* bsd);
+ void Response(Kernel::HLERequestContext& ctx);
+
+ s32 fd;
+ u32 flags;
+ std::vector<u8> message;
+ std::vector<u8> addr;
+ s32 ret{};
+ Errno bsd_errno{};
+ };
+
void RegisterClient(Kernel::HLERequestContext& ctx);
void StartMonitoring(Kernel::HLERequestContext& ctx);
void Socket(Kernel::HLERequestContext& ctx);
void Select(Kernel::HLERequestContext& ctx);
+ void Poll(Kernel::HLERequestContext& ctx);
+ void Accept(Kernel::HLERequestContext& ctx);
void Bind(Kernel::HLERequestContext& ctx);
void Connect(Kernel::HLERequestContext& ctx);
+ void GetPeerName(Kernel::HLERequestContext& ctx);
+ void GetSockName(Kernel::HLERequestContext& ctx);
void Listen(Kernel::HLERequestContext& ctx);
+ void Fcntl(Kernel::HLERequestContext& ctx);
void SetSockOpt(Kernel::HLERequestContext& ctx);
+ void Shutdown(Kernel::HLERequestContext& ctx);
+ void Recv(Kernel::HLERequestContext& ctx);
+ void RecvFrom(Kernel::HLERequestContext& ctx);
+ void Send(Kernel::HLERequestContext& ctx);
void SendTo(Kernel::HLERequestContext& ctx);
+ void Write(Kernel::HLERequestContext& ctx);
void Close(Kernel::HLERequestContext& ctx);
- /// Id to use for the next open file descriptor.
- u32 next_fd = 1;
+ template <typename Work>
+ void ExecuteWork(Kernel::HLERequestContext& ctx, std::string_view sleep_reason,
+ bool is_blocking, Work work);
+
+ std::pair<s32, Errno> SocketImpl(Domain domain, Type type, Protocol protocol);
+ std::pair<s32, Errno> PollImpl(std::vector<u8>& write_buffer, std::vector<u8> read_buffer,
+ s32 nfds, s32 timeout);
+ std::pair<s32, Errno> AcceptImpl(s32 fd, std::vector<u8>& write_buffer);
+ Errno BindImpl(s32 fd, const std::vector<u8>& addr);
+ Errno ConnectImpl(s32 fd, const std::vector<u8>& addr);
+ Errno GetPeerNameImpl(s32 fd, std::vector<u8>& write_buffer);
+ Errno GetSockNameImpl(s32 fd, std::vector<u8>& write_buffer);
+ Errno ListenImpl(s32 fd, s32 backlog);
+ std::pair<s32, Errno> FcntlImpl(s32 fd, FcntlCmd cmd, s32 arg);
+ Errno SetSockOptImpl(s32 fd, u32 level, OptName optname, size_t optlen, const void* optval);
+ Errno ShutdownImpl(s32 fd, s32 how);
+ std::pair<s32, Errno> RecvImpl(s32 fd, u32 flags, std::vector<u8>& message);
+ std::pair<s32, Errno> RecvFromImpl(s32 fd, u32 flags, std::vector<u8>& message,
+ std::vector<u8>& addr);
+ std::pair<s32, Errno> SendImpl(s32 fd, u32 flags, const std::vector<u8>& message);
+ std::pair<s32, Errno> SendToImpl(s32 fd, u32 flags, const std::vector<u8>& message,
+ const std::vector<u8>& addr);
+ Errno CloseImpl(s32 fd);
+
+ s32 FindFreeFileDescriptorHandle() noexcept;
+ bool IsFileDescriptorValid(s32 fd) const noexcept;
+ bool IsBlockingSocket(s32 fd) const noexcept;
+
+ void BuildErrnoResponse(Kernel::HLERequestContext& ctx, Errno bsd_errno) const noexcept;
+
+ std::array<std::optional<FileDescriptor>, MAX_FD> file_descriptors;
+
+ BlockingWorkerPool<BSD, PollWork, AcceptWork, ConnectWork, RecvWork, RecvFromWork, SendWork,
+ SendToWork>
+ worker_pool;
};
class BSDCFG final : public ServiceFramework<BSDCFG> {
diff --git a/src/core/hle/service/sockets/sockets.cpp b/src/core/hle/service/sockets/sockets.cpp
index 08d2d306a..1d27f7906 100644
--- a/src/core/hle/service/sockets/sockets.cpp
+++ b/src/core/hle/service/sockets/sockets.cpp
@@ -10,9 +10,9 @@
namespace Service::Sockets {
-void InstallInterfaces(SM::ServiceManager& service_manager) {
- std::make_shared<BSD>("bsd:s")->InstallAsService(service_manager);
- std::make_shared<BSD>("bsd:u")->InstallAsService(service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system) {
+ std::make_shared<BSD>(system, "bsd:s")->InstallAsService(service_manager);
+ std::make_shared<BSD>(system, "bsd:u")->InstallAsService(service_manager);
std::make_shared<BSDCFG>()->InstallAsService(service_manager);
std::make_shared<ETHC_C>()->InstallAsService(service_manager);
diff --git a/src/core/hle/service/sockets/sockets.h b/src/core/hle/service/sockets/sockets.h
index ca8a6a7e0..89a410076 100644
--- a/src/core/hle/service/sockets/sockets.h
+++ b/src/core/hle/service/sockets/sockets.h
@@ -4,11 +4,94 @@
#pragma once
+#include "common/common_types.h"
#include "core/hle/service/service.h"
+namespace Core {
+class System;
+}
+
namespace Service::Sockets {
+enum class Errno : u32 {
+ SUCCESS = 0,
+ BADF = 9,
+ AGAIN = 11,
+ INVAL = 22,
+ MFILE = 24,
+ NOTCONN = 107,
+};
+
+enum class Domain : u32 {
+ INET = 2,
+};
+
+enum class Type : u32 {
+ STREAM = 1,
+ DGRAM = 2,
+ RAW = 3,
+ SEQPACKET = 5,
+};
+
+enum class Protocol : u32 {
+ UNSPECIFIED = 0,
+ ICMP = 1,
+ TCP = 6,
+ UDP = 17,
+};
+
+enum class OptName : u32 {
+ REUSEADDR = 0x4,
+ BROADCAST = 0x20,
+ LINGER = 0x80,
+ SNDBUF = 0x1001,
+ RCVBUF = 0x1002,
+ SNDTIMEO = 0x1005,
+ RCVTIMEO = 0x1006,
+};
+
+enum class ShutdownHow : s32 {
+ RD = 0,
+ WR = 1,
+ RDWR = 2,
+};
+
+enum class FcntlCmd : s32 {
+ GETFL = 3,
+ SETFL = 4,
+};
+
+struct SockAddrIn {
+ u8 len;
+ u8 family;
+ u16 portno;
+ std::array<u8, 4> ip;
+ std::array<u8, 8> zeroes;
+};
+
+struct PollFD {
+ s32 fd;
+ u16 events;
+ u16 revents;
+};
+
+struct Linger {
+ u32 onoff;
+ u32 linger;
+};
+
+constexpr u16 POLL_IN = 0x01;
+constexpr u16 POLL_PRI = 0x02;
+constexpr u16 POLL_OUT = 0x04;
+constexpr u16 POLL_ERR = 0x08;
+constexpr u16 POLL_HUP = 0x10;
+constexpr u16 POLL_NVAL = 0x20;
+
+constexpr u32 FLAG_MSG_DONTWAIT = 0x80;
+
+constexpr u32 FLAG_O_NONBLOCK = 0x800;
+
/// Registers all Sockets services with the specified service manager.
-void InstallInterfaces(SM::ServiceManager& service_manager);
+void InstallInterfaces(SM::ServiceManager& service_manager, Core::System& system);
} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets_translate.cpp b/src/core/hle/service/sockets/sockets_translate.cpp
new file mode 100644
index 000000000..2e626fd86
--- /dev/null
+++ b/src/core/hle/service/sockets/sockets_translate.cpp
@@ -0,0 +1,165 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <utility>
+
+#include "common/assert.h"
+#include "common/common_types.h"
+#include "core/hle/service/sockets/sockets.h"
+#include "core/hle/service/sockets/sockets_translate.h"
+#include "core/network/network.h"
+
+namespace Service::Sockets {
+
+Errno Translate(Network::Errno value) {
+ switch (value) {
+ case Network::Errno::SUCCESS:
+ return Errno::SUCCESS;
+ case Network::Errno::BADF:
+ return Errno::BADF;
+ case Network::Errno::AGAIN:
+ return Errno::AGAIN;
+ case Network::Errno::INVAL:
+ return Errno::INVAL;
+ case Network::Errno::MFILE:
+ return Errno::MFILE;
+ case Network::Errno::NOTCONN:
+ return Errno::NOTCONN;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented errno={}", static_cast<int>(value));
+ return Errno::SUCCESS;
+ }
+}
+
+std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value) {
+ return {value.first, Translate(value.second)};
+}
+
+Network::Domain Translate(Domain domain) {
+ switch (domain) {
+ case Domain::INET:
+ return Network::Domain::INET;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
+ return {};
+ }
+}
+
+Domain Translate(Network::Domain domain) {
+ switch (domain) {
+ case Network::Domain::INET:
+ return Domain::INET;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented domain={}", static_cast<int>(domain));
+ return {};
+ }
+}
+
+Network::Type Translate(Type type) {
+ switch (type) {
+ case Type::STREAM:
+ return Network::Type::STREAM;
+ case Type::DGRAM:
+ return Network::Type::DGRAM;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented type={}", static_cast<int>(type));
+ }
+}
+
+Network::Protocol Translate(Type type, Protocol protocol) {
+ switch (protocol) {
+ case Protocol::UNSPECIFIED:
+ LOG_WARNING(Service, "Unspecified protocol, assuming protocol from type");
+ switch (type) {
+ case Type::DGRAM:
+ return Network::Protocol::UDP;
+ case Type::STREAM:
+ return Network::Protocol::TCP;
+ default:
+ return Network::Protocol::TCP;
+ }
+ case Protocol::TCP:
+ return Network::Protocol::TCP;
+ case Protocol::UDP:
+ return Network::Protocol::UDP;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented protocol={}", static_cast<int>(protocol));
+ return Network::Protocol::TCP;
+ }
+}
+
+u16 TranslatePollEventsToHost(u32 flags) {
+ u32 result = 0;
+ const auto translate = [&result, &flags](u32 from, u32 to) {
+ if ((flags & from) != 0) {
+ flags &= ~from;
+ result |= to;
+ }
+ };
+ translate(POLL_IN, Network::POLL_IN);
+ translate(POLL_PRI, Network::POLL_PRI);
+ translate(POLL_OUT, Network::POLL_OUT);
+ translate(POLL_ERR, Network::POLL_ERR);
+ translate(POLL_HUP, Network::POLL_HUP);
+ translate(POLL_NVAL, Network::POLL_NVAL);
+
+ UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
+ return static_cast<u16>(result);
+}
+
+u16 TranslatePollEventsToGuest(u32 flags) {
+ u32 result = 0;
+ const auto translate = [&result, &flags](u32 from, u32 to) {
+ if ((flags & from) != 0) {
+ flags &= ~from;
+ result |= to;
+ }
+ };
+
+ translate(Network::POLL_IN, POLL_IN);
+ translate(Network::POLL_PRI, POLL_PRI);
+ translate(Network::POLL_OUT, POLL_OUT);
+ translate(Network::POLL_ERR, POLL_ERR);
+ translate(Network::POLL_HUP, POLL_HUP);
+ translate(Network::POLL_NVAL, POLL_NVAL);
+
+ UNIMPLEMENTED_IF_MSG(flags != 0, "Unimplemented flags={}", flags);
+ return static_cast<u16>(result);
+}
+
+Network::SockAddrIn Translate(SockAddrIn value) {
+ ASSERT(value.len == 0 || value.len == sizeof(value));
+
+ return {
+ .family = Translate(static_cast<Domain>(value.family)),
+ .ip = value.ip,
+ .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
+ };
+}
+
+SockAddrIn Translate(Network::SockAddrIn value) {
+ return {
+ .len = sizeof(SockAddrIn),
+ .family = static_cast<u8>(Translate(value.family)),
+ .portno = static_cast<u16>(value.portno >> 8 | value.portno << 8),
+ .ip = value.ip,
+ .zeroes = {},
+ };
+}
+
+Network::ShutdownHow Translate(ShutdownHow how) {
+ switch (how) {
+ case ShutdownHow::RD:
+ return Network::ShutdownHow::RD;
+ case ShutdownHow::WR:
+ return Network::ShutdownHow::WR;
+ case ShutdownHow::RDWR:
+ return Network::ShutdownHow::RDWR;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented how={}", static_cast<int>(how));
+ return {};
+ }
+}
+
+} // namespace Service::Sockets
diff --git a/src/core/hle/service/sockets/sockets_translate.h b/src/core/hle/service/sockets/sockets_translate.h
new file mode 100644
index 000000000..e498913d4
--- /dev/null
+++ b/src/core/hle/service/sockets/sockets_translate.h
@@ -0,0 +1,48 @@
+// Copyright 2020 yuzu emulator team
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <utility>
+
+#include "common/common_types.h"
+#include "core/hle/service/sockets/sockets.h"
+#include "core/network/network.h"
+
+namespace Service::Sockets {
+
+/// Translate abstract errno to guest errno
+Errno Translate(Network::Errno value);
+
+/// Translate abstract return value errno pair to guest return value errno pair
+std::pair<s32, Errno> Translate(std::pair<s32, Network::Errno> value);
+
+/// Translate guest domain to abstract domain
+Network::Domain Translate(Domain domain);
+
+/// Translate abstract domain to guest domain
+Domain Translate(Network::Domain domain);
+
+/// Translate guest type to abstract type
+Network::Type Translate(Type type);
+
+/// Translate guest protocol to abstract protocol
+Network::Protocol Translate(Type type, Protocol protocol);
+
+/// Translate abstract poll event flags to guest poll event flags
+u16 TranslatePollEventsToHost(u32 flags);
+
+/// Translate guest poll event flags to abstract poll event flags
+u16 TranslatePollEventsToGuest(u32 flags);
+
+/// Translate guest socket address structure to abstract socket address structure
+Network::SockAddrIn Translate(SockAddrIn value);
+
+/// Translate abstract socket address structure to guest socket address structure
+SockAddrIn Translate(Network::SockAddrIn value);
+
+/// Translate guest shutdown mode to abstract shutdown mode
+Network::ShutdownHow Translate(ShutdownHow how);
+
+} // namespace Service::Sockets
diff --git a/src/core/hle/service/time/time.cpp b/src/core/hle/service/time/time.cpp
index ee4fa4b48..7d0474e0b 100644
--- a/src/core/hle/service/time/time.cpp
+++ b/src/core/hle/service/time/time.cpp
@@ -10,6 +10,7 @@
#include "core/hle/ipc_helpers.h"
#include "core/hle/kernel/client_port.h"
#include "core/hle/kernel/client_session.h"
+#include "core/hle/kernel/kernel.h"
#include "core/hle/kernel/scheduler.h"
#include "core/hle/service/time/interface.h"
#include "core/hle/service/time/time.h"
@@ -125,7 +126,7 @@ ResultCode Module::Interface::GetClockSnapshotFromSystemClockContextInternal(
Kernel::Thread* thread, Clock::SystemClockContext user_context,
Clock::SystemClockContext network_context, u8 type, Clock::ClockSnapshot& clock_snapshot) {
- auto& time_manager{module->GetTimeManager()};
+ auto& time_manager{system.GetTimeManager()};
clock_snapshot.is_automatic_correction_enabled =
time_manager.GetStandardUserSystemClockCore().IsAutomaticCorrectionEnabled();
@@ -182,7 +183,7 @@ void Module::Interface::GetStandardUserSystemClock(Kernel::HLERequestContext& ct
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardUserSystemClockCore(),
+ rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardUserSystemClockCore(),
system);
}
@@ -190,7 +191,7 @@ void Module::Interface::GetStandardNetworkSystemClock(Kernel::HLERequestContext&
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardNetworkSystemClockCore(),
+ rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardNetworkSystemClockCore(),
system);
}
@@ -198,29 +199,28 @@ void Module::Interface::GetStandardSteadyClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISteadyClock>(module->GetTimeManager().GetStandardSteadyClockCore(),
- system);
+ rb.PushIpcInterface<ISteadyClock>(system.GetTimeManager().GetStandardSteadyClockCore(), system);
}
void Module::Interface::GetTimeZoneService(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ITimeZoneService>(module->GetTimeManager().GetTimeZoneContentManager());
+ rb.PushIpcInterface<ITimeZoneService>(system.GetTimeManager().GetTimeZoneContentManager());
}
void Module::Interface::GetStandardLocalSystemClock(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 0, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushIpcInterface<ISystemClock>(module->GetTimeManager().GetStandardLocalSystemClockCore(),
+ rb.PushIpcInterface<ISystemClock>(system.GetTimeManager().GetStandardLocalSystemClockCore(),
system);
}
void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
- auto& clock_core{module->GetTimeManager().GetStandardNetworkSystemClockCore()};
+ auto& clock_core{system.GetTimeManager().GetStandardNetworkSystemClockCore()};
IPC::ResponseBuilder rb{ctx, 3};
rb.Push(RESULT_SUCCESS);
rb.Push<u32>(clock_core.IsStandardNetworkSystemClockAccuracySufficient(system));
@@ -229,7 +229,7 @@ void Module::Interface::IsStandardNetworkSystemClockAccuracySufficient(
void Module::Interface::CalculateMonotonicSystemClockBaseTimePoint(Kernel::HLERequestContext& ctx) {
LOG_DEBUG(Service_Time, "called");
- auto& steady_clock_core{module->GetTimeManager().GetStandardSteadyClockCore()};
+ auto& steady_clock_core{system.GetTimeManager().GetStandardSteadyClockCore()};
if (!steady_clock_core.IsInitialized()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(ERROR_UNINITIALIZED_CLOCK);
@@ -262,8 +262,8 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
Clock::SystemClockContext user_context{};
if (const ResultCode result{
- module->GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(
- system, user_context)};
+ system.GetTimeManager().GetStandardUserSystemClockCore().GetClockContext(system,
+ user_context)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
rb.Push(result);
@@ -271,7 +271,7 @@ void Module::Interface::GetClockSnapshot(Kernel::HLERequestContext& ctx) {
}
Clock::SystemClockContext network_context{};
if (const ResultCode result{
- module->GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
+ system.GetTimeManager().GetStandardNetworkSystemClockCore().GetClockContext(
system, network_context)};
result.IsError()) {
IPC::ResponseBuilder rb{ctx, 2};
@@ -372,7 +372,7 @@ void Module::Interface::GetSharedMemoryNativeHandle(Kernel::HLERequestContext& c
LOG_DEBUG(Service_Time, "called");
IPC::ResponseBuilder rb{ctx, 2, 1};
rb.Push(RESULT_SUCCESS);
- rb.PushCopyObjects(module->GetTimeManager().GetSharedMemory().GetSharedMemoryHolder());
+ rb.PushCopyObjects(SharedFrom(&system.Kernel().GetTimeSharedMem()));
}
Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& system, const char* name)
@@ -381,7 +381,7 @@ Module::Interface::Interface(std::shared_ptr<Module> module, Core::System& syste
Module::Interface::~Interface() = default;
void InstallInterfaces(Core::System& system) {
- auto module{std::make_shared<Module>(system)};
+ auto module{std::make_shared<Module>()};
std::make_shared<Time>(module, system, "time:a")->InstallAsService(system.ServiceManager());
std::make_shared<Time>(module, system, "time:s")->InstallAsService(system.ServiceManager());
std::make_shared<Time>(module, system, "time:u")->InstallAsService(system.ServiceManager());
diff --git a/src/core/hle/service/time/time.h b/src/core/hle/service/time/time.h
index 41f3002e9..49f4aac0a 100644
--- a/src/core/hle/service/time/time.h
+++ b/src/core/hle/service/time/time.h
@@ -16,7 +16,7 @@ namespace Service::Time {
class Module final {
public:
- Module(Core::System& system) : time_manager{system} {}
+ Module() = default;
class Interface : public ServiceFramework<Interface> {
public:
@@ -46,13 +46,6 @@ public:
std::shared_ptr<Module> module;
Core::System& system;
};
-
- TimeManager& GetTimeManager() {
- return time_manager;
- }
-
-private:
- TimeManager time_manager;
};
/// Registers all Time services with the specified service manager.
diff --git a/src/core/hle/service/time/time_manager.cpp b/src/core/hle/service/time/time_manager.cpp
index b4dfe45e5..858623e2b 100644
--- a/src/core/hle/service/time/time_manager.cpp
+++ b/src/core/hle/service/time/time_manager.cpp
@@ -22,125 +22,282 @@ static std::chrono::seconds GetSecondsSinceEpoch() {
Settings::values.custom_rtc_differential;
}
-static s64 GetExternalTimeZoneOffset() {
- // With "auto" timezone setting, we use the external system's timezone offset
- if (Settings::GetTimeZoneString() == "auto") {
- return Common::TimeZone::GetCurrentOffsetSeconds().count();
- }
- return 0;
-}
-
static s64 GetExternalRtcValue() {
- return GetSecondsSinceEpoch().count() + GetExternalTimeZoneOffset();
-}
-
-TimeManager::TimeManager(Core::System& system)
- : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
- standard_network_system_clock_core{standard_steady_clock_core},
- standard_user_system_clock_core{standard_local_system_clock_core,
- standard_network_system_clock_core, system},
- ephemeral_network_system_clock_core{tick_based_steady_clock_core},
- local_system_clock_context_writer{
- std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
- network_system_clock_context_writer{
- std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
- ephemeral_network_system_clock_context_writer{
- std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
- time_zone_content_manager{*this, system} {
-
- const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
- SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
- SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
- SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
- SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
- SetupEphemeralNetworkSystemClock();
+ return GetSecondsSinceEpoch().count() + TimeManager::GetExternalTimeZoneOffset();
}
-TimeManager::~TimeManager() = default;
+struct TimeManager::Impl final {
+ explicit Impl(Core::System& system)
+ : shared_memory{system}, standard_local_system_clock_core{standard_steady_clock_core},
+ standard_network_system_clock_core{standard_steady_clock_core},
+ standard_user_system_clock_core{standard_local_system_clock_core,
+ standard_network_system_clock_core, system},
+ ephemeral_network_system_clock_core{tick_based_steady_clock_core},
+ local_system_clock_context_writer{
+ std::make_shared<Clock::LocalSystemClockContextWriter>(shared_memory)},
+ network_system_clock_context_writer{
+ std::make_shared<Clock::NetworkSystemClockContextWriter>(shared_memory)},
+ ephemeral_network_system_clock_context_writer{
+ std::make_shared<Clock::EphemeralNetworkSystemClockContextWriter>()},
+ time_zone_content_manager{system} {
-void TimeManager::SetupTimeZoneManager(std::string location_name,
- Clock::SteadyClockTimePoint time_zone_updated_time_point,
- std::size_t total_location_name_count,
- u128 time_zone_rule_version,
- FileSys::VirtualFile& vfs_file) {
- if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
- location_name, vfs_file) != RESULT_SUCCESS) {
- UNREACHABLE();
- return;
- }
-
- time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
- time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
- total_location_name_count);
- time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(time_zone_rule_version);
- time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
-}
-
-void TimeManager::SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
- Clock::TimeSpanType setup_value,
- Clock::TimeSpanType internal_offset,
- bool is_rtc_reset_detected) {
- standard_steady_clock_core.SetClockSourceId(clock_source_id);
- standard_steady_clock_core.SetSetupValue(setup_value);
- standard_steady_clock_core.SetInternalOffset(internal_offset);
- standard_steady_clock_core.MarkAsInitialized();
-
- const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
- shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
-}
-
-void TimeManager::SetupStandardLocalSystemClock(Core::System& system,
- Clock::SystemClockContext clock_context,
- s64 posix_time) {
- standard_local_system_clock_core.SetUpdateCallbackInstance(local_system_clock_context_writer);
-
- const auto current_time_point{
- standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
- if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
- standard_local_system_clock_core.SetSystemClockContext(clock_context);
- } else {
- if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) != RESULT_SUCCESS) {
+ const auto system_time{Clock::TimeSpanType::FromSeconds(GetExternalRtcValue())};
+ SetupStandardSteadyClock(system, Common::UUID::Generate(), system_time, {}, {});
+ SetupStandardLocalSystemClock(system, {}, system_time.ToSeconds());
+ SetupStandardNetworkSystemClock({}, standard_network_clock_accuracy);
+ SetupStandardUserSystemClock(system, {}, Clock::SteadyClockTimePoint::GetRandom());
+ SetupEphemeralNetworkSystemClock();
+ }
+
+ ~Impl() = default;
+
+ Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
+ return standard_steady_clock_core;
+ }
+
+ const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
+ return standard_steady_clock_core;
+ }
+
+ Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
+ return standard_local_system_clock_core;
+ }
+
+ const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
+ return standard_local_system_clock_core;
+ }
+
+ Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
+ return standard_network_system_clock_core;
+ }
+
+ const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
+ return standard_network_system_clock_core;
+ }
+
+ Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
+ return standard_user_system_clock_core;
+ }
+
+ const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
+ return standard_user_system_clock_core;
+ }
+
+ TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
+ return time_zone_content_manager;
+ }
+
+ const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
+ return time_zone_content_manager;
+ }
+
+ SharedMemory& GetSharedMemory() {
+ return shared_memory;
+ }
+
+ const SharedMemory& GetSharedMemory() const {
+ return shared_memory;
+ }
+
+ void SetupTimeZoneManager(std::string location_name,
+ Clock::SteadyClockTimePoint time_zone_updated_time_point,
+ std::size_t total_location_name_count, u128 time_zone_rule_version,
+ FileSys::VirtualFile& vfs_file) {
+ if (time_zone_content_manager.GetTimeZoneManager().SetDeviceLocationNameWithTimeZoneRule(
+ location_name, vfs_file) != RESULT_SUCCESS) {
UNREACHABLE();
return;
}
+
+ time_zone_content_manager.GetTimeZoneManager().SetUpdatedTime(time_zone_updated_time_point);
+ time_zone_content_manager.GetTimeZoneManager().SetTotalLocationNameCount(
+ total_location_name_count);
+ time_zone_content_manager.GetTimeZoneManager().SetTimeZoneRuleVersion(
+ time_zone_rule_version);
+ time_zone_content_manager.GetTimeZoneManager().MarkAsInitialized();
}
- standard_local_system_clock_core.MarkAsInitialized();
-}
+ static s64 GetExternalTimeZoneOffset() {
+ // With "auto" timezone setting, we use the external system's timezone offset
+ if (Settings::GetTimeZoneString() == "auto") {
+ return Common::TimeZone::GetCurrentOffsetSeconds().count();
+ }
+ return 0;
+ }
-void TimeManager::SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
- Clock::TimeSpanType sufficient_accuracy) {
- standard_network_system_clock_core.SetUpdateCallbackInstance(
- network_system_clock_context_writer);
+ void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
+ Clock::TimeSpanType setup_value,
+ Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected) {
+ standard_steady_clock_core.SetClockSourceId(clock_source_id);
+ standard_steady_clock_core.SetSetupValue(setup_value);
+ standard_steady_clock_core.SetInternalOffset(internal_offset);
+ standard_steady_clock_core.MarkAsInitialized();
- if (standard_network_system_clock_core.SetSystemClockContext(clock_context) != RESULT_SUCCESS) {
- UNREACHABLE();
- return;
+ const auto current_time_point{standard_steady_clock_core.GetCurrentRawTimePoint(system)};
+ shared_memory.SetupStandardSteadyClock(system, clock_source_id, current_time_point);
}
- standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
- sufficient_accuracy);
- standard_network_system_clock_core.MarkAsInitialized();
-}
+ void SetupStandardLocalSystemClock(Core::System& system,
+ Clock::SystemClockContext clock_context, s64 posix_time) {
+ standard_local_system_clock_core.SetUpdateCallbackInstance(
+ local_system_clock_context_writer);
+
+ const auto current_time_point{
+ standard_local_system_clock_core.GetSteadyClockCore().GetCurrentTimePoint(system)};
+ if (current_time_point.clock_source_id == clock_context.steady_time_point.clock_source_id) {
+ standard_local_system_clock_core.SetSystemClockContext(clock_context);
+ } else {
+ if (standard_local_system_clock_core.SetCurrentTime(system, posix_time) !=
+ RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+ }
+
+ standard_local_system_clock_core.MarkAsInitialized();
+ }
+
+ void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
+ Clock::TimeSpanType sufficient_accuracy) {
+ standard_network_system_clock_core.SetUpdateCallbackInstance(
+ network_system_clock_context_writer);
-void TimeManager::SetupStandardUserSystemClock(
- Core::System& system, bool is_automatic_correction_enabled,
- Clock::SteadyClockTimePoint steady_clock_time_point) {
- if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
- system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
- UNREACHABLE();
- return;
+ if (standard_network_system_clock_core.SetSystemClockContext(clock_context) !=
+ RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+
+ standard_network_system_clock_core.SetStandardNetworkClockSufficientAccuracy(
+ sufficient_accuracy);
+ standard_network_system_clock_core.MarkAsInitialized();
}
- standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
- standard_user_system_clock_core.MarkAsInitialized();
- shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
+ void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
+ Clock::SteadyClockTimePoint steady_clock_time_point) {
+ if (standard_user_system_clock_core.SetAutomaticCorrectionEnabled(
+ system, is_automatic_correction_enabled) != RESULT_SUCCESS) {
+ UNREACHABLE();
+ return;
+ }
+
+ standard_user_system_clock_core.SetAutomaticCorrectionUpdatedTime(steady_clock_time_point);
+ standard_user_system_clock_core.MarkAsInitialized();
+ shared_memory.SetAutomaticCorrectionEnabled(is_automatic_correction_enabled);
+ }
+
+ void SetupEphemeralNetworkSystemClock() {
+ ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
+ ephemeral_network_system_clock_context_writer);
+ ephemeral_network_system_clock_core.MarkAsInitialized();
+ }
+
+ void UpdateLocalSystemClockTime(Core::System& system, s64 posix_time) {
+ const auto timespan{Service::Time::Clock::TimeSpanType::FromSeconds(posix_time)};
+ if (GetStandardLocalSystemClockCore()
+ .SetCurrentTime(system, timespan.ToSeconds())
+ .IsError()) {
+ UNREACHABLE();
+ return;
+ }
+ }
+
+ SharedMemory shared_memory;
+
+ Clock::StandardSteadyClockCore standard_steady_clock_core;
+ Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
+ Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
+ Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
+ Clock::StandardUserSystemClockCore standard_user_system_clock_core;
+ Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
+
+ std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
+ std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
+ std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
+ ephemeral_network_system_clock_context_writer;
+
+ TimeZone::TimeZoneContentManager time_zone_content_manager;
+};
+
+TimeManager::TimeManager(Core::System& system) : system{system} {}
+
+TimeManager::~TimeManager() = default;
+
+void TimeManager::Initialize() {
+ impl = std::make_unique<Impl>(system);
+
+ // Time zones can only be initialized after impl is valid
+ impl->time_zone_content_manager.Initialize(*this);
+}
+
+Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() {
+ return impl->standard_steady_clock_core;
+}
+
+const Clock::StandardSteadyClockCore& TimeManager::GetStandardSteadyClockCore() const {
+ return impl->standard_steady_clock_core;
+}
+
+Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() {
+ return impl->standard_local_system_clock_core;
+}
+
+const Clock::StandardLocalSystemClockCore& TimeManager::GetStandardLocalSystemClockCore() const {
+ return impl->standard_local_system_clock_core;
+}
+
+Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore() {
+ return impl->standard_network_system_clock_core;
}
-void TimeManager::SetupEphemeralNetworkSystemClock() {
- ephemeral_network_system_clock_core.SetUpdateCallbackInstance(
- ephemeral_network_system_clock_context_writer);
- ephemeral_network_system_clock_core.MarkAsInitialized();
+const Clock::StandardNetworkSystemClockCore& TimeManager::GetStandardNetworkSystemClockCore()
+ const {
+ return impl->standard_network_system_clock_core;
+}
+
+Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() {
+ return impl->standard_user_system_clock_core;
+}
+
+const Clock::StandardUserSystemClockCore& TimeManager::GetStandardUserSystemClockCore() const {
+ return impl->standard_user_system_clock_core;
+}
+
+TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() {
+ return impl->time_zone_content_manager;
+}
+
+const TimeZone::TimeZoneContentManager& TimeManager::GetTimeZoneContentManager() const {
+ return impl->time_zone_content_manager;
+}
+
+SharedMemory& TimeManager::GetSharedMemory() {
+ return impl->shared_memory;
+}
+
+const SharedMemory& TimeManager::GetSharedMemory() const {
+ return impl->shared_memory;
+}
+
+void TimeManager::UpdateLocalSystemClockTime(s64 posix_time) {
+ impl->UpdateLocalSystemClockTime(system, posix_time);
+}
+
+void TimeManager::SetupTimeZoneManager(std::string location_name,
+ Clock::SteadyClockTimePoint time_zone_updated_time_point,
+ std::size_t total_location_name_count,
+ u128 time_zone_rule_version,
+ FileSys::VirtualFile& vfs_file) {
+ impl->SetupTimeZoneManager(location_name, time_zone_updated_time_point,
+ total_location_name_count, time_zone_rule_version, vfs_file);
+}
+
+/*static*/ s64 TimeManager::GetExternalTimeZoneOffset() {
+ // With "auto" timezone setting, we use the external system's timezone offset
+ if (Settings::GetTimeZoneString() == "auto") {
+ return Common::TimeZone::GetCurrentOffsetSeconds().count();
+ }
+ return 0;
}
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_manager.h b/src/core/hle/service/time/time_manager.h
index 8e65f0d22..993c7c288 100644
--- a/src/core/hle/service/time/time_manager.h
+++ b/src/core/hle/service/time/time_manager.h
@@ -5,6 +5,7 @@
#pragma once
#include "common/common_types.h"
+#include "common/time_zone.h"
#include "core/file_sys/vfs_types.h"
#include "core/hle/service/time/clock_types.h"
#include "core/hle/service/time/ephemeral_network_system_clock_core.h"
@@ -32,86 +33,46 @@ public:
explicit TimeManager(Core::System& system);
~TimeManager();
- Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() {
- return standard_steady_clock_core;
- }
+ void Initialize();
- const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const {
- return standard_steady_clock_core;
- }
+ Clock::StandardSteadyClockCore& GetStandardSteadyClockCore();
- Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() {
- return standard_local_system_clock_core;
- }
+ const Clock::StandardSteadyClockCore& GetStandardSteadyClockCore() const;
- const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const {
- return standard_local_system_clock_core;
- }
+ Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore();
- Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() {
- return standard_network_system_clock_core;
- }
+ const Clock::StandardLocalSystemClockCore& GetStandardLocalSystemClockCore() const;
- const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const {
- return standard_network_system_clock_core;
- }
+ Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore();
- Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() {
- return standard_user_system_clock_core;
- }
+ const Clock::StandardNetworkSystemClockCore& GetStandardNetworkSystemClockCore() const;
- const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const {
- return standard_user_system_clock_core;
- }
+ Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore();
- TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() {
- return time_zone_content_manager;
- }
+ const Clock::StandardUserSystemClockCore& GetStandardUserSystemClockCore() const;
- const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const {
- return time_zone_content_manager;
- }
+ TimeZone::TimeZoneContentManager& GetTimeZoneContentManager();
- SharedMemory& GetSharedMemory() {
- return shared_memory;
- }
+ const TimeZone::TimeZoneContentManager& GetTimeZoneContentManager() const;
- const SharedMemory& GetSharedMemory() const {
- return shared_memory;
- }
+ void UpdateLocalSystemClockTime(s64 posix_time);
+
+ SharedMemory& GetSharedMemory();
+
+ const SharedMemory& GetSharedMemory() const;
void SetupTimeZoneManager(std::string location_name,
Clock::SteadyClockTimePoint time_zone_updated_time_point,
std::size_t total_location_name_count, u128 time_zone_rule_version,
FileSys::VirtualFile& vfs_file);
+ static s64 GetExternalTimeZoneOffset();
+
private:
- void SetupStandardSteadyClock(Core::System& system, Common::UUID clock_source_id,
- Clock::TimeSpanType setup_value,
- Clock::TimeSpanType internal_offset, bool is_rtc_reset_detected);
- void SetupStandardLocalSystemClock(Core::System& system,
- Clock::SystemClockContext clock_context, s64 posix_time);
- void SetupStandardNetworkSystemClock(Clock::SystemClockContext clock_context,
- Clock::TimeSpanType sufficient_accuracy);
- void SetupStandardUserSystemClock(Core::System& system, bool is_automatic_correction_enabled,
- Clock::SteadyClockTimePoint steady_clock_time_point);
- void SetupEphemeralNetworkSystemClock();
-
- SharedMemory shared_memory;
-
- Clock::StandardSteadyClockCore standard_steady_clock_core;
- Clock::TickBasedSteadyClockCore tick_based_steady_clock_core;
- Clock::StandardLocalSystemClockCore standard_local_system_clock_core;
- Clock::StandardNetworkSystemClockCore standard_network_system_clock_core;
- Clock::StandardUserSystemClockCore standard_user_system_clock_core;
- Clock::EphemeralNetworkSystemClockCore ephemeral_network_system_clock_core;
-
- std::shared_ptr<Clock::LocalSystemClockContextWriter> local_system_clock_context_writer;
- std::shared_ptr<Clock::NetworkSystemClockContextWriter> network_system_clock_context_writer;
- std::shared_ptr<Clock::EphemeralNetworkSystemClockContextWriter>
- ephemeral_network_system_clock_context_writer;
-
- TimeZone::TimeZoneContentManager time_zone_content_manager;
+ Core::System& system;
+
+ struct Impl;
+ std::unique_ptr<Impl> impl;
};
} // namespace Service::Time
diff --git a/src/core/hle/service/time/time_zone_content_manager.cpp b/src/core/hle/service/time/time_zone_content_manager.cpp
index 320672add..4177d0a41 100644
--- a/src/core/hle/service/time/time_zone_content_manager.cpp
+++ b/src/core/hle/service/time/time_zone_content_manager.cpp
@@ -68,9 +68,10 @@ static std::vector<std::string> BuildLocationNameCache(Core::System& system) {
return location_name_cache;
}
-TimeZoneContentManager::TimeZoneContentManager(TimeManager& time_manager, Core::System& system)
- : system{system}, location_name_cache{BuildLocationNameCache(system)} {
+TimeZoneContentManager::TimeZoneContentManager(Core::System& system)
+ : system{system}, location_name_cache{BuildLocationNameCache(system)} {}
+void TimeZoneContentManager::Initialize(TimeManager& time_manager) {
std::string location_name;
const auto timezone_setting = Settings::GetTimeZoneString();
if (timezone_setting == "auto" || timezone_setting == "default") {
diff --git a/src/core/hle/service/time/time_zone_content_manager.h b/src/core/hle/service/time/time_zone_content_manager.h
index 4f302c3b9..52dd1a020 100644
--- a/src/core/hle/service/time/time_zone_content_manager.h
+++ b/src/core/hle/service/time/time_zone_content_manager.h
@@ -21,7 +21,9 @@ namespace Service::Time::TimeZone {
class TimeZoneContentManager final {
public:
- TimeZoneContentManager(TimeManager& time_manager, Core::System& system);
+ explicit TimeZoneContentManager(Core::System& system);
+
+ void Initialize(TimeManager& time_manager);
TimeZoneManager& GetTimeZoneManager() {
return time_zone_manager;
diff --git a/src/core/hle/service/time/time_zone_manager.cpp b/src/core/hle/service/time/time_zone_manager.cpp
index 69152d0ac..bdf0439f2 100644
--- a/src/core/hle/service/time/time_zone_manager.cpp
+++ b/src/core/hle/service/time/time_zone_manager.cpp
@@ -820,7 +820,10 @@ static ResultCode ToCalendarTimeImpl(const TimeZoneRule& rules, s64 time, Calend
const ResultCode result{
ToCalendarTimeInternal(rules, time, calendar_time, calendar.additiona_info)};
calendar.time.year = static_cast<s16>(calendar_time.year);
- calendar.time.month = calendar_time.month + 1; // Internal impl. uses 0-indexed month
+
+ // Internal impl. uses 0-indexed month
+ calendar.time.month = static_cast<s8>(calendar_time.month + 1);
+
calendar.time.day = calendar_time.day;
calendar.time.hour = calendar_time.hour;
calendar.time.minute = calendar_time.minute;
@@ -872,13 +875,15 @@ ResultCode TimeZoneManager::ToPosixTime(const TimeZoneRule& rules,
const CalendarTime& calendar_time, s64& posix_time) const {
posix_time = 0;
- CalendarTimeInternal internal_time{};
- internal_time.year = calendar_time.year;
- internal_time.month = calendar_time.month - 1; // Internal impl. uses 0-indexed month
- internal_time.day = calendar_time.day;
- internal_time.hour = calendar_time.hour;
- internal_time.minute = calendar_time.minute;
- internal_time.second = calendar_time.second;
+ CalendarTimeInternal internal_time{
+ .year = calendar_time.year,
+ // Internal impl. uses 0-indexed month
+ .month = static_cast<s8>(calendar_time.month - 1),
+ .day = calendar_time.day,
+ .hour = calendar_time.hour,
+ .minute = calendar_time.minute,
+ .second = calendar_time.second,
+ };
s32 hour{internal_time.hour};
s32 minute{internal_time.minute};
diff --git a/src/core/hle/service/vi/vi.cpp b/src/core/hle/service/vi/vi.cpp
index 480d34725..55e00dd93 100644
--- a/src/core/hle/service/vi/vi.cpp
+++ b/src/core/hle/service/vi/vi.cpp
@@ -159,7 +159,7 @@ public:
header.data_size = static_cast<u32_le>(write_index - sizeof(Header));
header.data_offset = sizeof(Header);
header.objects_size = 4;
- header.objects_offset = sizeof(Header) + header.data_size;
+ header.objects_offset = static_cast<u32>(sizeof(Header) + header.data_size);
std::memcpy(buffer.data(), &header, sizeof(Header));
return buffer;
@@ -215,10 +215,9 @@ public:
explicit IGBPConnectRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPConnectRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -279,10 +278,9 @@ public:
: Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPSetPreallocatedBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
buffer = Read<NVFlinger::IGBPBuffer>();
}
@@ -306,15 +304,40 @@ protected:
}
};
+class IGBPCancelBufferRequestParcel : public Parcel {
+public:
+ explicit IGBPCancelBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
+ Deserialize();
+ }
+
+ void DeserializeData() override {
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
+ data = Read<Data>();
+ }
+
+ struct Data {
+ u32_le slot;
+ Service::Nvidia::MultiFence multi_fence;
+ };
+
+ Data data;
+};
+
+class IGBPCancelBufferResponseParcel : public Parcel {
+protected:
+ void SerializeData() override {
+ Write<u32>(0); // Success
+ }
+};
+
class IGBPDequeueBufferRequestParcel : public Parcel {
public:
explicit IGBPDequeueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPDequeueBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -333,7 +356,6 @@ class IGBPDequeueBufferResponseParcel : public Parcel {
public:
explicit IGBPDequeueBufferResponseParcel(u32 slot, Service::Nvidia::MultiFence& multi_fence)
: slot(slot), multi_fence(multi_fence) {}
- ~IGBPDequeueBufferResponseParcel() override = default;
protected:
void SerializeData() override {
@@ -352,10 +374,9 @@ public:
explicit IGBPRequestBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPRequestBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
slot = Read<u32_le>();
}
@@ -384,10 +405,9 @@ public:
explicit IGBPQueueBufferRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPQueueBufferRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
data = Read<Data>();
}
@@ -447,10 +467,9 @@ public:
explicit IGBPQueryRequestParcel(std::vector<u8> buffer) : Parcel(std::move(buffer)) {
Deserialize();
}
- ~IGBPQueryRequestParcel() override = default;
void DeserializeData() override {
- std::u16string token = ReadInterfaceToken();
+ [[maybe_unused]] const std::u16string token = ReadInterfaceToken();
type = Read<u32_le>();
}
@@ -596,7 +615,12 @@ private:
break;
}
case TransactionId::CancelBuffer: {
- LOG_CRITICAL(Service_VI, "(STUBBED) called, transaction=CancelBuffer");
+ IGBPCancelBufferRequestParcel request{ctx.ReadBuffer()};
+
+ buffer_queue.CancelBuffer(request.data.slot, request.data.multi_fence);
+
+ IGBPCancelBufferResponseParcel response{};
+ ctx.WriteBuffer(response.Serialize());
break;
}
case TransactionId::Disconnect: {
@@ -747,7 +771,7 @@ private:
IPC::ResponseBuilder rb{ctx, 6};
rb.Push(RESULT_SUCCESS);
- if (Settings::values.use_docked_mode) {
+ if (Settings::values.use_docked_mode.GetValue()) {
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedWidth) *
static_cast<u32>(Settings::values.resolution_factor.GetValue()));
rb.Push(static_cast<u32>(Service::VI::DisplayResolution::DockedHeight) *
diff --git a/src/core/loader/deconstructed_rom_directory.cpp b/src/core/loader/deconstructed_rom_directory.cpp
index 134e83412..2002dc4f2 100644
--- a/src/core/loader/deconstructed_rom_directory.cpp
+++ b/src/core/loader/deconstructed_rom_directory.cpp
@@ -89,7 +89,7 @@ FileType AppLoader_DeconstructedRomDirectory::IdentifyType(const FileSys::Virtua
}
AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirectory::Load(
- Kernel::Process& process) {
+ Kernel::Process& process, Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
@@ -114,7 +114,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
if (override_update) {
- const FileSys::PatchManager patch_manager(metadata.GetTitleID());
+ const FileSys::PatchManager patch_manager(
+ metadata.GetTitleID(), system.GetFileSystemController(), system.GetContentProvider());
dir = patch_manager.PatchExeFS(dir);
}
@@ -141,9 +142,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
continue;
}
- const bool should_pass_arguments{std::strcmp(module, "rtld") == 0};
- const auto tentative_next_load_addr{AppLoader_NSO::LoadModule(
- process, *module_file, code_size, should_pass_arguments, false)};
+ const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
+ const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
+ process, system, *module_file, code_size, should_pass_arguments, false);
if (!tentative_next_load_addr) {
return {ResultStatus::ErrorLoadingNSO, {}};
}
@@ -160,7 +161,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
modules.clear();
const VAddr base_address{process.PageTable().GetCodeRegionStart()};
VAddr next_load_addr{base_address};
- const FileSys::PatchManager pm{metadata.GetTitleID()};
+ const FileSys::PatchManager pm{metadata.GetTitleID(), system.GetFileSystemController(),
+ system.GetContentProvider()};
for (const auto& module : static_modules) {
const FileSys::VirtualFile module_file{dir->GetFile(module)};
if (!module_file) {
@@ -168,9 +170,9 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
}
const VAddr load_addr{next_load_addr};
- const bool should_pass_arguments{std::strcmp(module, "rtld") == 0};
- const auto tentative_next_load_addr{AppLoader_NSO::LoadModule(
- process, *module_file, load_addr, should_pass_arguments, true, pm)};
+ const bool should_pass_arguments = std::strcmp(module, "rtld") == 0;
+ const auto tentative_next_load_addr = AppLoader_NSO::LoadModule(
+ process, system, *module_file, load_addr, should_pass_arguments, true, pm);
if (!tentative_next_load_addr) {
return {ResultStatus::ErrorLoadingNSO, {}};
}
@@ -192,8 +194,8 @@ AppLoader_DeconstructedRomDirectory::LoadResult AppLoader_DeconstructedRomDirect
// Register the RomFS if a ".romfs" file was found
if (romfs_iter != files.end() && *romfs_iter != nullptr) {
romfs = *romfs_iter;
- Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
- std::make_unique<FileSys::RomFSFactory>(*this));
+ system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
+ *this, system.GetContentProvider(), system.GetFileSystemController()));
}
is_loaded = true;
diff --git a/src/core/loader/deconstructed_rom_directory.h b/src/core/loader/deconstructed_rom_directory.h
index 1c0a354a4..35d340317 100644
--- a/src/core/loader/deconstructed_rom_directory.h
+++ b/src/core/loader/deconstructed_rom_directory.h
@@ -9,6 +9,10 @@
#include "core/file_sys/program_metadata.h"
#include "core/loader/loader.h"
+namespace Core {
+class System;
+}
+
namespace Loader {
/**
@@ -37,7 +41,7 @@ public:
return IdentifyType(file);
}
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
diff --git a/src/core/loader/elf.cpp b/src/core/loader/elf.cpp
index 8f7615115..dca1fcb18 100644
--- a/src/core/loader/elf.cpp
+++ b/src/core/loader/elf.cpp
@@ -383,7 +383,8 @@ FileType AppLoader_ELF::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process) {
+AppLoader_ELF::LoadResult AppLoader_ELF::Load(Kernel::Process& process,
+ [[maybe_unused]] Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
diff --git a/src/core/loader/elf.h b/src/core/loader/elf.h
index 7ef7770a6..3527933ad 100644
--- a/src/core/loader/elf.h
+++ b/src/core/loader/elf.h
@@ -8,6 +8,10 @@
#include "common/common_types.h"
#include "core/loader/loader.h"
+namespace Core {
+class System;
+}
+
namespace Loader {
/// Loads an ELF/AXF file
@@ -26,7 +30,7 @@ public:
return IdentifyType(file);
}
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
};
} // namespace Loader
diff --git a/src/core/loader/kip.cpp b/src/core/loader/kip.cpp
index 40fa03ad1..2a905d3e4 100644
--- a/src/core/loader/kip.cpp
+++ b/src/core/loader/kip.cpp
@@ -16,7 +16,7 @@ namespace Loader {
namespace {
constexpr u32 PageAlignSize(u32 size) {
- return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK;
+ return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
}
} // Anonymous namespace
@@ -43,7 +43,8 @@ FileType AppLoader_KIP::GetFileType() const {
: FileType::Error;
}
-AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process) {
+AppLoader::LoadResult AppLoader_KIP::Load(Kernel::Process& process,
+ [[maybe_unused]] Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
diff --git a/src/core/loader/kip.h b/src/core/loader/kip.h
index 12ca40269..dee05a7b5 100644
--- a/src/core/loader/kip.h
+++ b/src/core/loader/kip.h
@@ -6,6 +6,10 @@
#include "core/loader/loader.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class KIP;
}
@@ -26,7 +30,7 @@ public:
FileType GetFileType() const override;
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
private:
std::unique_ptr<FileSys::KIP> kip;
diff --git a/src/core/loader/loader.cpp b/src/core/loader/loader.cpp
index 9bc3a8840..deffe7379 100644
--- a/src/core/loader/loader.cpp
+++ b/src/core/loader/loader.cpp
@@ -10,6 +10,7 @@
#include "common/file_util.h"
#include "common/logging/log.h"
#include "common/string_util.h"
+#include "core/core.h"
#include "core/hle/kernel/process.h"
#include "core/loader/deconstructed_rom_directory.h"
#include "core/loader/elf.h"
@@ -194,15 +195,14 @@ AppLoader::~AppLoader() = default;
/**
* Get a loader for a file with a specific type
- * @param file The file to load
- * @param type The type of the file
- * @param file the file to retrieve the loader for
- * @param type the file type
+ * @param system The system context to use.
+ * @param file The file to retrieve the loader for
+ * @param type The file type
* @return std::unique_ptr<AppLoader> a pointer to a loader object; nullptr for unsupported type
*/
-static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileType type) {
+static std::unique_ptr<AppLoader> GetFileLoader(Core::System& system, FileSys::VirtualFile file,
+ FileType type) {
switch (type) {
-
// Standard ELF file format.
case FileType::ELF:
return std::make_unique<AppLoader_ELF>(std::move(file));
@@ -221,7 +221,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
// NX XCI (nX Card Image) file format.
case FileType::XCI:
- return std::make_unique<AppLoader_XCI>(std::move(file));
+ return std::make_unique<AppLoader_XCI>(std::move(file), system.GetFileSystemController(),
+ system.GetContentProvider());
// NX NAX (NintendoAesXts) file format.
case FileType::NAX:
@@ -229,7 +230,8 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
// NX NSP (Nintendo Submission Package) file format
case FileType::NSP:
- return std::make_unique<AppLoader_NSP>(std::move(file));
+ return std::make_unique<AppLoader_NSP>(std::move(file), system.GetFileSystemController(),
+ system.GetContentProvider());
// NX KIP (Kernel Internal Process) file format
case FileType::KIP:
@@ -244,20 +246,21 @@ static std::unique_ptr<AppLoader> GetFileLoader(FileSys::VirtualFile file, FileT
}
}
-std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file) {
+std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file) {
FileType type = IdentifyFile(file);
- FileType filename_type = GuessFromFilename(file->GetName());
+ const FileType filename_type = GuessFromFilename(file->GetName());
// Special case: 00 is either a NCA or NAX.
if (type != filename_type && !(file->GetName() == "00" && type == FileType::NAX)) {
LOG_WARNING(Loader, "File {} has a different type than its extension.", file->GetName());
- if (FileType::Unknown == type)
+ if (FileType::Unknown == type) {
type = filename_type;
+ }
}
LOG_DEBUG(Loader, "Loading file {} as {}...", file->GetName(), GetFileTypeString(type));
- return GetFileLoader(std::move(file), type);
+ return GetFileLoader(system, std::move(file), type);
}
} // namespace Loader
diff --git a/src/core/loader/loader.h b/src/core/loader/loader.h
index 227ecc704..8dc2d7615 100644
--- a/src/core/loader/loader.h
+++ b/src/core/loader/loader.h
@@ -15,6 +15,10 @@
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/vfs.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class NACP;
} // namespace FileSys
@@ -154,9 +158,10 @@ public:
/**
* Load the application and return the created Process instance
* @param process The newly created process.
+ * @param system The system that this process is being loaded under.
* @return The status result of the operation.
*/
- virtual LoadResult Load(Kernel::Process& process) = 0;
+ virtual LoadResult Load(Kernel::Process& process, Core::System& system) = 0;
/**
* Get the code (typically .code section) of the application
@@ -285,9 +290,12 @@ protected:
/**
* Identifies a bootable file and return a suitable loader
- * @param file The bootable file
- * @return the best loader for this file
+ *
+ * @param system The system context.
+ * @param file The bootable file.
+ *
+ * @return the best loader for this file.
*/
-std::unique_ptr<AppLoader> GetLoader(FileSys::VirtualFile file);
+std::unique_ptr<AppLoader> GetLoader(Core::System& system, FileSys::VirtualFile file);
} // namespace Loader
diff --git a/src/core/loader/nax.cpp b/src/core/loader/nax.cpp
index a152981a0..49028177b 100644
--- a/src/core/loader/nax.cpp
+++ b/src/core/loader/nax.cpp
@@ -41,7 +41,8 @@ FileType AppLoader_NAX::GetFileType() const {
return IdentifyTypeImpl(*nax);
}
-AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
+AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process,
+ [[maybe_unused]] Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
@@ -65,7 +66,7 @@ AppLoader_NAX::LoadResult AppLoader_NAX::Load(Kernel::Process& process) {
return {nca_status, {}};
}
- const auto result = nca_loader->Load(process);
+ const auto result = nca_loader->Load(process, system);
if (result.first != ResultStatus::Success) {
return result;
}
diff --git a/src/core/loader/nax.h b/src/core/loader/nax.h
index eaec9bf58..c2b7722b5 100644
--- a/src/core/loader/nax.h
+++ b/src/core/loader/nax.h
@@ -8,10 +8,12 @@
#include "common/common_types.h"
#include "core/loader/loader.h"
-namespace FileSys {
+namespace Core {
+class System;
+}
+namespace FileSys {
class NAX;
-
} // namespace FileSys
namespace Loader {
@@ -33,7 +35,7 @@ public:
FileType GetFileType() const override;
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nca.cpp b/src/core/loader/nca.cpp
index 5a0469978..fa694de37 100644
--- a/src/core/loader/nca.cpp
+++ b/src/core/loader/nca.cpp
@@ -31,7 +31,7 @@ FileType AppLoader_NCA::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
+AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process, Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
@@ -52,14 +52,14 @@ AppLoader_NCA::LoadResult AppLoader_NCA::Load(Kernel::Process& process) {
directory_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(exefs, true);
- const auto load_result = directory_loader->Load(process);
+ const auto load_result = directory_loader->Load(process, system);
if (load_result.first != ResultStatus::Success) {
return load_result;
}
if (nca->GetRomFS() != nullptr && nca->GetRomFS()->GetSize() > 0) {
- Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
- std::make_unique<FileSys::RomFSFactory>(*this));
+ system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
+ *this, system.GetContentProvider(), system.GetFileSystemController()));
}
is_loaded = true;
diff --git a/src/core/loader/nca.h b/src/core/loader/nca.h
index e47dc0e47..711070294 100644
--- a/src/core/loader/nca.h
+++ b/src/core/loader/nca.h
@@ -8,6 +8,10 @@
#include "core/file_sys/vfs.h"
#include "core/loader/loader.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class NCA;
}
@@ -33,7 +37,7 @@ public:
return IdentifyType(file);
}
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& dir) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/nro.cpp b/src/core/loader/nro.cpp
index 906544bc9..5f4b3104b 100644
--- a/src/core/loader/nro.cpp
+++ b/src/core/loader/nro.cpp
@@ -127,7 +127,7 @@ FileType AppLoader_NRO::IdentifyType(const FileSys::VirtualFile& file) {
}
static constexpr u32 PageAlignSize(u32 size) {
- return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK;
+ return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
}
static bool LoadNroImpl(Kernel::Process& process, const std::vector<u8>& data,
@@ -208,7 +208,7 @@ bool AppLoader_NRO::LoadNro(Kernel::Process& process, const FileSys::VfsFile& fi
return LoadNroImpl(process, file.ReadAllBytes(), file.GetName());
}
-AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
+AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process, Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
@@ -218,8 +218,8 @@ AppLoader_NRO::LoadResult AppLoader_NRO::Load(Kernel::Process& process) {
}
if (romfs != nullptr) {
- Core::System::GetInstance().GetFileSystemController().RegisterRomFS(
- std::make_unique<FileSys::RomFSFactory>(*this));
+ system.GetFileSystemController().RegisterRomFS(std::make_unique<FileSys::RomFSFactory>(
+ *this, system.GetContentProvider(), system.GetFileSystemController()));
}
is_loaded = true;
diff --git a/src/core/loader/nro.h b/src/core/loader/nro.h
index 4593d48fb..a2aab2ecc 100644
--- a/src/core/loader/nro.h
+++ b/src/core/loader/nro.h
@@ -10,6 +10,10 @@
#include "common/common_types.h"
#include "core/loader/loader.h"
+namespace Core {
+class System;
+}
+
namespace FileSys {
class NACP;
}
@@ -37,7 +41,7 @@ public:
return IdentifyType(file);
}
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadIcon(std::vector<u8>& buffer) override;
ResultStatus ReadProgramId(u64& out_program_id) override;
diff --git a/src/core/loader/nso.cpp b/src/core/loader/nso.cpp
index 575330a86..aa85c1a29 100644
--- a/src/core/loader/nso.cpp
+++ b/src/core/loader/nso.cpp
@@ -47,7 +47,7 @@ std::vector<u8> DecompressSegment(const std::vector<u8>& compressed_data,
}
constexpr u32 PageAlignSize(u32 size) {
- return (size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK;
+ return static_cast<u32>((size + Core::Memory::PAGE_MASK) & ~Core::Memory::PAGE_MASK);
}
} // Anonymous namespace
@@ -71,21 +71,21 @@ FileType AppLoader_NSO::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::NSO;
}
-std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
+std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process, Core::System& system,
const FileSys::VfsFile& file, VAddr load_base,
bool should_pass_arguments, bool load_into_process,
std::optional<FileSys::PatchManager> pm) {
if (file.GetSize() < sizeof(NSOHeader)) {
- return {};
+ return std::nullopt;
}
NSOHeader nso_header{};
if (sizeof(NSOHeader) != file.ReadObject(&nso_header)) {
- return {};
+ return std::nullopt;
}
if (nso_header.magic != Common::MakeMagic('N', 'S', 'O', '0')) {
- return {};
+ return std::nullopt;
}
// Build program image
@@ -148,9 +148,8 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
// Apply cheats if they exist and the program has a valid title ID
if (pm) {
- auto& system = Core::System::GetInstance();
system.SetCurrentProcessBuildID(nso_header.build_id);
- const auto cheats = pm->CreateCheatList(system, nso_header.build_id);
+ const auto cheats = pm->CreateCheatList(nso_header.build_id);
if (!cheats.empty()) {
system.RegisterCheatList(cheats, nso_header.build_id, load_base, image_size);
}
@@ -166,7 +165,7 @@ std::optional<VAddr> AppLoader_NSO::LoadModule(Kernel::Process& process,
return load_base + image_size;
}
-AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
+AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process, Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
@@ -175,7 +174,7 @@ AppLoader_NSO::LoadResult AppLoader_NSO::Load(Kernel::Process& process) {
// Load module
const VAddr base_address = process.PageTable().GetCodeRegionStart();
- if (!LoadModule(process, *file, base_address, true, true)) {
+ if (!LoadModule(process, system, *file, base_address, true, true)) {
return {ResultStatus::ErrorLoadingNSO, {}};
}
diff --git a/src/core/loader/nso.h b/src/core/loader/nso.h
index b210830f0..d331096ae 100644
--- a/src/core/loader/nso.h
+++ b/src/core/loader/nso.h
@@ -12,6 +12,10 @@
#include "core/file_sys/patch_manager.h"
#include "core/loader/loader.h"
+namespace Core {
+class System;
+}
+
namespace Kernel {
class Process;
}
@@ -55,7 +59,7 @@ struct NSOHeader {
static_assert(sizeof(NSOHeader) == 0x100, "NSOHeader has incorrect size.");
static_assert(std::is_trivially_copyable_v<NSOHeader>, "NSOHeader must be trivially copyable.");
-constexpr u64 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
+constexpr u32 NSO_ARGUMENT_DATA_ALLOCATION_SIZE = 0x9000;
struct NSOArgumentHeader {
u32_le allocated_size;
@@ -80,12 +84,12 @@ public:
return IdentifyType(file);
}
- static std::optional<VAddr> LoadModule(Kernel::Process& process, const FileSys::VfsFile& file,
- VAddr load_base, bool should_pass_arguments,
- bool load_into_process,
+ static std::optional<VAddr> LoadModule(Kernel::Process& process, Core::System& system,
+ const FileSys::VfsFile& file, VAddr load_base,
+ bool should_pass_arguments, bool load_into_process,
std::optional<FileSys::PatchManager> pm = {});
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadNSOModules(Modules& modules) override;
diff --git a/src/core/loader/nsp.cpp b/src/core/loader/nsp.cpp
index 13950fc08..e821937fd 100644
--- a/src/core/loader/nsp.cpp
+++ b/src/core/loader/nsp.cpp
@@ -21,26 +21,33 @@
namespace Loader {
-AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file)
+AppLoader_NSP::AppLoader_NSP(FileSys::VirtualFile file,
+ const Service::FileSystem::FileSystemController& fsc,
+ const FileSys::ContentProvider& content_provider)
: AppLoader(file), nsp(std::make_unique<FileSys::NSP>(file)),
title_id(nsp->GetProgramTitleID()) {
- if (nsp->GetStatus() != ResultStatus::Success)
+ if (nsp->GetStatus() != ResultStatus::Success) {
return;
+ }
if (nsp->IsExtractedType()) {
secondary_loader = std::make_unique<AppLoader_DeconstructedRomDirectory>(nsp->GetExeFS());
} else {
const auto control_nca =
nsp->GetNCA(nsp->GetProgramTitleID(), FileSys::ContentRecordType::Control);
- if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
+ if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
return;
+ }
- std::tie(nacp_file, icon_file) =
- FileSys::PatchManager(nsp->GetProgramTitleID()).ParseControlNCA(*control_nca);
+ std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
+ const FileSys::PatchManager pm{nsp->GetProgramTitleID(), fsc, content_provider};
+ return pm.ParseControlNCA(*control_nca);
+ }();
- if (title_id == 0)
+ if (title_id == 0) {
return;
+ }
secondary_loader = std::make_unique<AppLoader_NCA>(
nsp->GetNCAFile(title_id, FileSys::ContentRecordType::Program));
@@ -71,7 +78,7 @@ FileType AppLoader_NSP::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
+AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process, Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
@@ -99,15 +106,14 @@ AppLoader_NSP::LoadResult AppLoader_NSP::Load(Kernel::Process& process) {
return {ResultStatus::ErrorNSPMissingProgramNCA, {}};
}
- const auto result = secondary_loader->Load(process);
+ const auto result = secondary_loader->Load(process, system);
if (result.first != ResultStatus::Success) {
return result;
}
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
- Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
- std::move(update_raw));
+ system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
}
is_loaded = true;
diff --git a/src/core/loader/nsp.h b/src/core/loader/nsp.h
index 868b028d3..36e8e3533 100644
--- a/src/core/loader/nsp.h
+++ b/src/core/loader/nsp.h
@@ -10,10 +10,15 @@
#include "core/loader/loader.h"
namespace FileSys {
+class ContentProvider;
class NACP;
class NSP;
} // namespace FileSys
+namespace Service::FileSystem {
+class FileSystemController;
+}
+
namespace Loader {
class AppLoader_NCA;
@@ -21,7 +26,9 @@ class AppLoader_NCA;
/// Loads an XCI file
class AppLoader_NSP final : public AppLoader {
public:
- explicit AppLoader_NSP(FileSys::VirtualFile file);
+ explicit AppLoader_NSP(FileSys::VirtualFile file,
+ const Service::FileSystem::FileSystemController& fsc,
+ const FileSys::ContentProvider& content_provider);
~AppLoader_NSP() override;
/**
@@ -35,7 +42,7 @@ public:
return IdentifyType(file);
}
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/loader/xci.cpp b/src/core/loader/xci.cpp
index 7186ad1ff..536e721fc 100644
--- a/src/core/loader/xci.cpp
+++ b/src/core/loader/xci.cpp
@@ -20,18 +20,24 @@
namespace Loader {
-AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file)
+AppLoader_XCI::AppLoader_XCI(FileSys::VirtualFile file,
+ const Service::FileSystem::FileSystemController& fsc,
+ const FileSys::ContentProvider& content_provider)
: AppLoader(file), xci(std::make_unique<FileSys::XCI>(file)),
nca_loader(std::make_unique<AppLoader_NCA>(xci->GetProgramNCAFile())) {
- if (xci->GetStatus() != ResultStatus::Success)
+ if (xci->GetStatus() != ResultStatus::Success) {
return;
+ }
const auto control_nca = xci->GetNCAByType(FileSys::NCAContentType::Control);
- if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success)
+ if (control_nca == nullptr || control_nca->GetStatus() != ResultStatus::Success) {
return;
+ }
- std::tie(nacp_file, icon_file) =
- FileSys::PatchManager(xci->GetProgramTitleID()).ParseControlNCA(*control_nca);
+ std::tie(nacp_file, icon_file) = [this, &content_provider, &control_nca, &fsc] {
+ const FileSys::PatchManager pm{xci->GetProgramTitleID(), fsc, content_provider};
+ return pm.ParseControlNCA(*control_nca);
+ }();
}
AppLoader_XCI::~AppLoader_XCI() = default;
@@ -49,7 +55,7 @@ FileType AppLoader_XCI::IdentifyType(const FileSys::VirtualFile& file) {
return FileType::Error;
}
-AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
+AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process, Core::System& system) {
if (is_loaded) {
return {ResultStatus::ErrorAlreadyLoaded, {}};
}
@@ -66,15 +72,14 @@ AppLoader_XCI::LoadResult AppLoader_XCI::Load(Kernel::Process& process) {
return {ResultStatus::ErrorMissingProductionKeyFile, {}};
}
- const auto result = nca_loader->Load(process);
+ const auto result = nca_loader->Load(process, system);
if (result.first != ResultStatus::Success) {
return result;
}
FileSys::VirtualFile update_raw;
if (ReadUpdateRaw(update_raw) == ResultStatus::Success && update_raw != nullptr) {
- Core::System::GetInstance().GetFileSystemController().SetPackedUpdate(
- std::move(update_raw));
+ system.GetFileSystemController().SetPackedUpdate(std::move(update_raw));
}
is_loaded = true;
diff --git a/src/core/loader/xci.h b/src/core/loader/xci.h
index 618ae2f47..6dc1f9243 100644
--- a/src/core/loader/xci.h
+++ b/src/core/loader/xci.h
@@ -10,10 +10,15 @@
#include "core/loader/loader.h"
namespace FileSys {
+class ContentProvider;
class NACP;
class XCI;
} // namespace FileSys
+namespace Service::FileSystem {
+class FileSystemController;
+}
+
namespace Loader {
class AppLoader_NCA;
@@ -21,7 +26,9 @@ class AppLoader_NCA;
/// Loads an XCI file
class AppLoader_XCI final : public AppLoader {
public:
- explicit AppLoader_XCI(FileSys::VirtualFile file);
+ explicit AppLoader_XCI(FileSys::VirtualFile file,
+ const Service::FileSystem::FileSystemController& fsc,
+ const FileSys::ContentProvider& content_provider);
~AppLoader_XCI() override;
/**
@@ -35,7 +42,7 @@ public:
return IdentifyType(file);
}
- LoadResult Load(Kernel::Process& process) override;
+ LoadResult Load(Kernel::Process& process, Core::System& system) override;
ResultStatus ReadRomFS(FileSys::VirtualFile& file) override;
u64 ReadRomFSIVFCOffset() const override;
diff --git a/src/core/memory.cpp b/src/core/memory.cpp
index 86d17c6cb..b88aa5c40 100644
--- a/src/core/memory.cpp
+++ b/src/core/memory.cpp
@@ -120,9 +120,9 @@ struct Memory::Impl {
if ((addr & 1) == 0) {
return Read<u16_le>(addr);
} else {
- const u8 a{Read<u8>(addr)};
- const u8 b{Read<u8>(addr + sizeof(u8))};
- return (static_cast<u16>(b) << 8) | a;
+ const u32 a{Read<u8>(addr)};
+ const u32 b{Read<u8>(addr + sizeof(u8))};
+ return static_cast<u16>((b << 8) | a);
}
}
@@ -130,9 +130,9 @@ struct Memory::Impl {
if ((addr & 3) == 0) {
return Read<u32_le>(addr);
} else {
- const u16 a{Read16(addr)};
- const u16 b{Read16(addr + sizeof(u16))};
- return (static_cast<u32>(b) << 16) | a;
+ const u32 a{Read16(addr)};
+ const u32 b{Read16(addr + sizeof(u16))};
+ return (b << 16) | a;
}
}
@@ -567,7 +567,7 @@ struct Memory::Impl {
* @param page_table The page table to use to perform the mapping.
* @param base The base address to begin mapping at.
* @param size The total size of the range in bytes.
- * @param memory The memory to map.
+ * @param target The target address to begin mapping from.
* @param type The page type to map the memory as.
*/
void MapPages(Common::PageTable& page_table, VAddr base, u64 size, PAddr target,
diff --git a/src/core/memory/cheat_engine.cpp b/src/core/memory/cheat_engine.cpp
index e503118dd..2dd0eb0f8 100644
--- a/src/core/memory/cheat_engine.cpp
+++ b/src/core/memory/cheat_engine.cpp
@@ -19,10 +19,24 @@
#include "core/memory/cheat_engine.h"
namespace Core::Memory {
-
+namespace {
constexpr auto CHEAT_ENGINE_NS = std::chrono::nanoseconds{1000000000 / 12};
constexpr u32 KEYPAD_BITMASK = 0x3FFFFFF;
+std::string_view ExtractName(std::string_view data, std::size_t start_index, char match) {
+ auto end_index = start_index;
+ while (data[end_index] != match) {
+ ++end_index;
+ if (end_index > data.size() ||
+ (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
+ return {};
+ }
+ }
+
+ return data.substr(start_index, end_index - start_index);
+}
+} // Anonymous namespace
+
StandardVmCallbacks::StandardVmCallbacks(Core::System& system, const CheatProcessMetadata& metadata)
: metadata(metadata), system(system) {}
@@ -82,26 +96,9 @@ CheatParser::~CheatParser() = default;
TextCheatParser::~TextCheatParser() = default;
-namespace {
-template <char match>
-std::string_view ExtractName(std::string_view data, std::size_t start_index) {
- auto end_index = start_index;
- while (data[end_index] != match) {
- ++end_index;
- if (end_index > data.size() ||
- (end_index - start_index - 1) > sizeof(CheatDefinition::readable_name)) {
- return {};
- }
- }
-
- return data.substr(start_index, end_index - start_index);
-}
-} // Anonymous namespace
-
-std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
- std::string_view data) const {
+std::vector<CheatEntry> TextCheatParser::Parse(std::string_view data) const {
std::vector<CheatEntry> out(1);
- std::optional<u64> current_entry = std::nullopt;
+ std::optional<u64> current_entry;
for (std::size_t i = 0; i < data.size(); ++i) {
if (::isspace(data[i])) {
@@ -115,7 +112,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
return {};
}
- const auto name = ExtractName<'}'>(data, i + 1);
+ const auto name = ExtractName(data, i + 1, '}');
if (name.empty()) {
return {};
}
@@ -132,7 +129,7 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
current_entry = out.size();
out.emplace_back();
- const auto name = ExtractName<']'>(data, i + 1);
+ const auto name = ExtractName(data, i + 1, ']');
if (name.empty()) {
return {};
}
@@ -156,8 +153,9 @@ std::vector<CheatEntry> TextCheatParser::Parse(const Core::System& system,
return {};
}
+ const auto value = static_cast<u32>(std::stoul(hex, nullptr, 0x10));
out[*current_entry].definition.opcodes[out[*current_entry].definition.num_opcodes++] =
- std::stoul(hex, nullptr, 0x10);
+ value;
i += 8;
} else {
diff --git a/src/core/memory/cheat_engine.h b/src/core/memory/cheat_engine.h
index fa039a831..a31002346 100644
--- a/src/core/memory/cheat_engine.h
+++ b/src/core/memory/cheat_engine.h
@@ -47,8 +47,7 @@ class CheatParser {
public:
virtual ~CheatParser();
- virtual std::vector<CheatEntry> Parse(const Core::System& system,
- std::string_view data) const = 0;
+ [[nodiscard]] virtual std::vector<CheatEntry> Parse(std::string_view data) const = 0;
};
// CheatParser implementation that parses text files
@@ -56,7 +55,7 @@ class TextCheatParser final : public CheatParser {
public:
~TextCheatParser() override;
- std::vector<CheatEntry> Parse(const Core::System& system, std::string_view data) const override;
+ [[nodiscard]] std::vector<CheatEntry> Parse(std::string_view data) const override;
};
// Class that encapsulates a CheatList and manages its interaction with memory and CoreTiming
diff --git a/src/core/network/network.cpp b/src/core/network/network.cpp
index 56d173b5e..5ef2e8511 100644
--- a/src/core/network/network.cpp
+++ b/src/core/network/network.cpp
@@ -148,7 +148,7 @@ sockaddr TranslateFromSockAddrIn(SockAddrIn input) {
}
int WSAPoll(WSAPOLLFD* fds, ULONG nfds, int timeout) {
- return poll(fds, nfds, timeout);
+ return poll(fds, static_cast<nfds_t>(nfds), timeout);
}
int closesocket(SOCKET fd) {
@@ -238,14 +238,14 @@ SockAddrIn TranslateToSockAddrIn(sockaddr input_) {
return result;
}
-u16 TranslatePollEvents(u16 events) {
- u16 result = 0;
+u16 TranslatePollEvents(u32 events) {
+ u32 result = 0;
- if (events & POLL_IN) {
+ if ((events & POLL_IN) != 0) {
events &= ~POLL_IN;
result |= POLLIN;
}
- if (events & POLL_PRI) {
+ if ((events & POLL_PRI) != 0) {
events &= ~POLL_PRI;
#ifdef _WIN32
LOG_WARNING(Service, "Winsock doesn't support POLLPRI");
@@ -253,20 +253,20 @@ u16 TranslatePollEvents(u16 events) {
result |= POLL_PRI;
#endif
}
- if (events & POLL_OUT) {
+ if ((events & POLL_OUT) != 0) {
events &= ~POLL_OUT;
result |= POLLOUT;
}
UNIMPLEMENTED_IF_MSG(events != 0, "Unhandled guest events=0x{:x}", events);
- return result;
+ return static_cast<u16>(result);
}
-u16 TranslatePollRevents(u16 revents) {
- u16 result = 0;
- const auto translate = [&result, &revents](int host, unsigned guest) {
- if (revents & host) {
+u16 TranslatePollRevents(u32 revents) {
+ u32 result = 0;
+ const auto translate = [&result, &revents](u32 host, u32 guest) {
+ if ((revents & host) != 0) {
revents &= ~host;
result |= guest;
}
@@ -280,7 +280,7 @@ u16 TranslatePollRevents(u16 revents) {
UNIMPLEMENTED_IF_MSG(revents != 0, "Unhandled host revents=0x{:x}", revents);
- return result;
+ return static_cast<u16>(result);
}
template <typename T>
@@ -350,7 +350,7 @@ std::pair<s32, Errno> Poll(std::vector<PollFD>& pollfds, s32 timeout) {
}
for (size_t i = 0; i < num; ++i) {
- pollfds[i].revents = TranslatePollRevents(host_pollfds[i].revents);
+ pollfds[i].revents = TranslatePollRevents(static_cast<u32>(host_pollfds[i].revents));
}
if (result > 0) {
@@ -408,7 +408,7 @@ std::pair<Socket::AcceptResult, Errno> Socket::Accept() {
Errno Socket::Connect(SockAddrIn addr_in) {
const sockaddr host_addr_in = TranslateFromSockAddrIn(addr_in);
- if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != INVALID_SOCKET) {
+ if (connect(fd, &host_addr_in, sizeof(host_addr_in)) != SOCKET_ERROR) {
return Errno::SUCCESS;
}
@@ -503,10 +503,10 @@ std::pair<s32, Errno> Socket::Recv(int flags, std::vector<u8>& message) {
ASSERT(flags == 0);
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
- const int result =
+ const auto result =
recv(fd, reinterpret_cast<char*>(message.data()), static_cast<int>(message.size()), 0);
if (result != SOCKET_ERROR) {
- return {result, Errno::SUCCESS};
+ return {static_cast<s32>(result), Errno::SUCCESS};
}
switch (const int ec = LastError()) {
@@ -531,14 +531,14 @@ std::pair<s32, Errno> Socket::RecvFrom(int flags, std::vector<u8>& message, Sock
socklen_t* const p_addrlen = addr ? &addrlen : nullptr;
sockaddr* const p_addr_in = addr ? &addr_in : nullptr;
- const int result = recvfrom(fd, reinterpret_cast<char*>(message.data()),
- static_cast<int>(message.size()), 0, p_addr_in, p_addrlen);
+ const auto result = recvfrom(fd, reinterpret_cast<char*>(message.data()),
+ static_cast<int>(message.size()), 0, p_addr_in, p_addrlen);
if (result != SOCKET_ERROR) {
if (addr) {
ASSERT(addrlen == sizeof(addr_in));
*addr = TranslateToSockAddrIn(addr_in);
}
- return {result, Errno::SUCCESS};
+ return {static_cast<s32>(result), Errno::SUCCESS};
}
switch (const int ec = LastError()) {
@@ -558,10 +558,10 @@ std::pair<s32, Errno> Socket::Send(const std::vector<u8>& message, int flags) {
ASSERT(message.size() < static_cast<size_t>(std::numeric_limits<int>::max()));
ASSERT(flags == 0);
- const int result = send(fd, reinterpret_cast<const char*>(message.data()),
- static_cast<int>(message.size()), 0);
+ const auto result = send(fd, reinterpret_cast<const char*>(message.data()),
+ static_cast<int>(message.size()), 0);
if (result != SOCKET_ERROR) {
- return {result, Errno::SUCCESS};
+ return {static_cast<s32>(result), Errno::SUCCESS};
}
const int ec = LastError();
@@ -591,10 +591,10 @@ std::pair<s32, Errno> Socket::SendTo(u32 flags, const std::vector<u8>& message,
to = &host_addr_in;
}
- const int result = sendto(fd, reinterpret_cast<const char*>(message.data()),
- static_cast<int>(message.size()), 0, to, tolen);
+ const auto result = sendto(fd, reinterpret_cast<const char*>(message.data()),
+ static_cast<int>(message.size()), 0, to, tolen);
if (result != SOCKET_ERROR) {
- return {result, Errno::SUCCESS};
+ return {static_cast<s32>(result), Errno::SUCCESS};
}
const int ec = LastError();
diff --git a/src/core/settings.cpp b/src/core/settings.cpp
index d328fb8b7..aadbc3932 100644
--- a/src/core/settings.cpp
+++ b/src/core/settings.cpp
@@ -13,58 +13,8 @@
namespace Settings {
-namespace NativeButton {
-const std::array<const char*, NumButtons> mapping = {{
- "button_a",
- "button_b",
- "button_x",
- "button_y",
- "button_lstick",
- "button_rstick",
- "button_l",
- "button_r",
- "button_zl",
- "button_zr",
- "button_plus",
- "button_minus",
- "button_dleft",
- "button_dup",
- "button_dright",
- "button_ddown",
- "button_lstick_left",
- "button_lstick_up",
- "button_lstick_right",
- "button_lstick_down",
- "button_rstick_left",
- "button_rstick_up",
- "button_rstick_right",
- "button_rstick_down",
- "button_sl",
- "button_sr",
- "button_home",
- "button_screenshot",
-}};
-}
-
-namespace NativeAnalog {
-const std::array<const char*, NumAnalogs> mapping = {{
- "lstick",
- "rstick",
-}};
-}
-
-namespace NativeMouseButton {
-const std::array<const char*, NumMouseButtons> mapping = {{
- "left",
- "right",
- "middle",
- "forward",
- "back",
-}};
-}
-
Values values = {};
-bool configuring_global = true;
+static bool configuring_global = true;
std::string GetTimeZoneString() {
static constexpr std::array timezones{
@@ -99,13 +49,14 @@ void LogSettings() {
};
LOG_INFO(Config, "yuzu Configuration:");
- log_setting("Controls_UseDockedMode", values.use_docked_mode);
+ log_setting("Controls_UseDockedMode", values.use_docked_mode.GetValue());
log_setting("System_RngSeed", values.rng_seed.GetValue().value_or(0));
log_setting("System_CurrentUser", values.current_user);
log_setting("System_LanguageIndex", values.language_index.GetValue());
log_setting("System_RegionIndex", values.region_index.GetValue());
log_setting("System_TimeZoneIndex", values.time_zone_index.GetValue());
log_setting("Core_UseMultiCore", values.use_multi_core.GetValue());
+ log_setting("CPU_Accuracy", values.cpu_accuracy);
log_setting("Renderer_UseResolutionFactor", values.resolution_factor.GetValue());
log_setting("Renderer_UseFrameLimit", values.use_frame_limit.GetValue());
log_setting("Renderer_FrameLimit", values.frame_limit.GetValue());
@@ -113,6 +64,7 @@ void LogSettings() {
log_setting("Renderer_GPUAccuracyLevel", values.gpu_accuracy.GetValue());
log_setting("Renderer_UseAsynchronousGpuEmulation",
values.use_asynchronous_gpu_emulation.GetValue());
+ log_setting("Renderer_UseNvdecEmulation", values.use_nvdec_emulation.GetValue());
log_setting("Renderer_UseVsync", values.use_vsync.GetValue());
log_setting("Renderer_UseAssemblyShaders", values.use_assembly_shaders.GetValue());
log_setting("Renderer_UseAsynchronousShaders", values.use_asynchronous_shaders.GetValue());
@@ -130,11 +82,12 @@ void LogSettings() {
log_setting("Services_BCATBoxcatLocal", values.bcat_boxcat_local);
}
-float Volume() {
- if (values.audio_muted) {
- return 0.0f;
- }
- return values.volume.GetValue();
+bool IsConfiguringGlobal() {
+ return configuring_global;
+}
+
+void SetConfiguringGlobal(bool is_global) {
+ configuring_global = is_global;
}
bool IsGPULevelExtreme() {
@@ -146,6 +99,13 @@ bool IsGPULevelHigh() {
values.gpu_accuracy.GetValue() == GPUAccuracy::High;
}
+float Volume() {
+ if (values.audio_muted) {
+ return 0.0f;
+ }
+ return values.volume.GetValue();
+}
+
void RestoreGlobalState() {
// If a game is running, DO NOT restore the global settings state
if (Core::System::GetInstance().IsPoweredOn()) {
@@ -169,6 +129,7 @@ void RestoreGlobalState() {
values.use_disk_shader_cache.SetGlobal(true);
values.gpu_accuracy.SetGlobal(true);
values.use_asynchronous_gpu_emulation.SetGlobal(true);
+ values.use_nvdec_emulation.SetGlobal(true);
values.use_vsync.SetGlobal(true);
values.use_assembly_shaders.SetGlobal(true);
values.use_asynchronous_shaders.SetGlobal(true);
@@ -184,6 +145,12 @@ void RestoreGlobalState() {
values.rng_seed.SetGlobal(true);
values.custom_rtc.SetGlobal(true);
values.sound_index.SetGlobal(true);
+
+ // Controls
+ values.players.SetGlobal(true);
+ values.use_docked_mode.SetGlobal(true);
+ values.vibration_enabled.SetGlobal(true);
+ values.motion_enabled.SetGlobal(true);
}
void Sanitize() {
diff --git a/src/core/settings.h b/src/core/settings.h
index 5a2f852fd..1143aba5d 100644
--- a/src/core/settings.h
+++ b/src/core/settings.h
@@ -12,340 +12,10 @@
#include <string>
#include <vector>
#include "common/common_types.h"
+#include "input_common/settings.h"
namespace Settings {
-namespace NativeButton {
-enum Values {
- A,
- B,
- X,
- Y,
- LStick,
- RStick,
- L,
- R,
- ZL,
- ZR,
- Plus,
- Minus,
-
- DLeft,
- DUp,
- DRight,
- DDown,
-
- LStick_Left,
- LStick_Up,
- LStick_Right,
- LStick_Down,
-
- RStick_Left,
- RStick_Up,
- RStick_Right,
- RStick_Down,
-
- SL,
- SR,
-
- Home,
- Screenshot,
-
- NumButtons,
-};
-
-constexpr int BUTTON_HID_BEGIN = A;
-constexpr int BUTTON_NS_BEGIN = Home;
-
-constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
-constexpr int BUTTON_NS_END = NumButtons;
-
-constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
-constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
-
-extern const std::array<const char*, NumButtons> mapping;
-
-} // namespace NativeButton
-
-namespace NativeAnalog {
-enum Values {
- LStick,
- RStick,
-
- NumAnalogs,
-};
-
-constexpr int STICK_HID_BEGIN = LStick;
-constexpr int STICK_HID_END = NumAnalogs;
-constexpr int NUM_STICKS_HID = NumAnalogs;
-
-extern const std::array<const char*, NumAnalogs> mapping;
-} // namespace NativeAnalog
-
-namespace NativeMouseButton {
-enum Values {
- Left,
- Right,
- Middle,
- Forward,
- Back,
-
- NumMouseButtons,
-};
-
-constexpr int MOUSE_HID_BEGIN = Left;
-constexpr int MOUSE_HID_END = NumMouseButtons;
-constexpr int NUM_MOUSE_HID = NumMouseButtons;
-
-extern const std::array<const char*, NumMouseButtons> mapping;
-} // namespace NativeMouseButton
-
-namespace NativeKeyboard {
-enum Keys {
- None,
- Error,
-
- A = 4,
- B,
- C,
- D,
- E,
- F,
- G,
- H,
- I,
- J,
- K,
- L,
- M,
- N,
- O,
- P,
- Q,
- R,
- S,
- T,
- U,
- V,
- W,
- X,
- Y,
- Z,
- N1,
- N2,
- N3,
- N4,
- N5,
- N6,
- N7,
- N8,
- N9,
- N0,
- Enter,
- Escape,
- Backspace,
- Tab,
- Space,
- Minus,
- Equal,
- LeftBrace,
- RightBrace,
- Backslash,
- Tilde,
- Semicolon,
- Apostrophe,
- Grave,
- Comma,
- Dot,
- Slash,
- CapsLockKey,
-
- F1,
- F2,
- F3,
- F4,
- F5,
- F6,
- F7,
- F8,
- F9,
- F10,
- F11,
- F12,
-
- SystemRequest,
- ScrollLockKey,
- Pause,
- Insert,
- Home,
- PageUp,
- Delete,
- End,
- PageDown,
- Right,
- Left,
- Down,
- Up,
-
- NumLockKey,
- KPSlash,
- KPAsterisk,
- KPMinus,
- KPPlus,
- KPEnter,
- KP1,
- KP2,
- KP3,
- KP4,
- KP5,
- KP6,
- KP7,
- KP8,
- KP9,
- KP0,
- KPDot,
-
- Key102,
- Compose,
- Power,
- KPEqual,
-
- F13,
- F14,
- F15,
- F16,
- F17,
- F18,
- F19,
- F20,
- F21,
- F22,
- F23,
- F24,
-
- Open,
- Help,
- Properties,
- Front,
- Stop,
- Repeat,
- Undo,
- Cut,
- Copy,
- Paste,
- Find,
- Mute,
- VolumeUp,
- VolumeDown,
- CapsLockActive,
- NumLockActive,
- ScrollLockActive,
- KPComma,
-
- KPLeftParenthesis,
- KPRightParenthesis,
-
- LeftControlKey = 0xE0,
- LeftShiftKey,
- LeftAltKey,
- LeftMetaKey,
- RightControlKey,
- RightShiftKey,
- RightAltKey,
- RightMetaKey,
-
- MediaPlayPause,
- MediaStopCD,
- MediaPrevious,
- MediaNext,
- MediaEject,
- MediaVolumeUp,
- MediaVolumeDown,
- MediaMute,
- MediaWebsite,
- MediaBack,
- MediaForward,
- MediaStop,
- MediaFind,
- MediaScrollUp,
- MediaScrollDown,
- MediaEdit,
- MediaSleep,
- MediaCoffee,
- MediaRefresh,
- MediaCalculator,
-
- NumKeyboardKeys,
-};
-
-static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
-
-enum Modifiers {
- LeftControl,
- LeftShift,
- LeftAlt,
- LeftMeta,
- RightControl,
- RightShift,
- RightAlt,
- RightMeta,
- CapsLock,
- ScrollLock,
- NumLock,
-
- NumKeyboardMods,
-};
-
-constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
-constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
-constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
-
-constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
-constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
-constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
-
-} // namespace NativeKeyboard
-
-using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
-using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
-using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
-using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
-using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
-
-constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
-constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
-constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
-constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
-
-enum class ControllerType {
- ProController,
- DualJoycon,
- RightJoycon,
- LeftJoycon,
-};
-
-struct PlayerInput {
- bool connected;
- ControllerType type;
- ButtonsRaw buttons;
- AnalogsRaw analogs;
-
- u32 body_color_right;
- u32 button_color_right;
- u32 body_color_left;
- u32 button_color_left;
-};
-
-struct TouchscreenInput {
- bool enabled;
- std::string device;
-
- u32 finger;
- u32 diameter_x;
- u32 diameter_y;
- u32 rotation_angle;
-};
-
enum class RendererBackend {
OpenGL = 0,
Vulkan = 1,
@@ -363,8 +33,6 @@ enum class CPUAccuracy {
DebugMode = 2,
};
-extern bool configuring_global;
-
template <typename Type>
class Setting final {
public:
@@ -397,6 +65,43 @@ private:
Type local{};
};
+/**
+ * The InputSetting class allows for getting a reference to either the global or local members.
+ * This is required as we cannot easily modify the values of user-defined types within containers
+ * using the SetValue() member function found in the Setting class. The primary purpose of this
+ * class is to store an array of 10 PlayerInput structs for both the global and local (per-game)
+ * setting and allows for easily accessing and modifying both settings.
+ */
+template <typename Type>
+class InputSetting final {
+public:
+ InputSetting() = default;
+ explicit InputSetting(Type val) : global{val} {}
+ ~InputSetting() = default;
+ void SetGlobal(bool to_global) {
+ use_global = to_global;
+ }
+ bool UsingGlobal() const {
+ return use_global;
+ }
+ Type& GetValue(bool need_global = false) {
+ if (use_global || need_global) {
+ return global;
+ }
+ return local;
+ }
+
+private:
+ bool use_global = true;
+ Type global{};
+ Type local{};
+};
+
+struct TouchFromButtonMap {
+ std::string name;
+ std::vector<std::string> buttons;
+};
+
struct Values {
// Audio
std::string audio_device_id;
@@ -428,7 +133,7 @@ struct Values {
bool renderer_debug;
Setting<int> vulkan_device;
- Setting<u16> resolution_factor = Setting(static_cast<u16>(1));
+ Setting<u16> resolution_factor{1};
Setting<int> aspect_ratio;
Setting<int> max_anisotropy;
Setting<bool> use_frame_limit;
@@ -436,6 +141,7 @@ struct Values {
Setting<bool> use_disk_shader_cache;
Setting<GPUAccuracy> gpu_accuracy;
Setting<bool> use_asynchronous_gpu_emulation;
+ Setting<bool> use_nvdec_emulation;
Setting<bool> use_vsync;
Setting<bool> use_assembly_shaders;
Setting<bool> use_asynchronous_shaders;
@@ -459,7 +165,18 @@ struct Values {
Setting<s32> sound_index;
// Controls
- std::array<PlayerInput, 10> players;
+ InputSetting<std::array<PlayerInput, 10>> players;
+
+ Setting<bool> use_docked_mode;
+
+ Setting<bool> vibration_enabled;
+ Setting<bool> enable_accurate_vibrations;
+
+ Setting<bool> motion_enabled;
+ std::string motion_device;
+ std::string udp_input_address;
+ u16 udp_input_port;
+ u8 udp_pad_index;
bool mouse_enabled;
std::string mouse_device;
@@ -473,14 +190,14 @@ struct Values {
ButtonsRaw debug_pad_buttons;
AnalogsRaw debug_pad_analogs;
- std::string motion_device;
TouchscreenInput touchscreen;
- std::atomic_bool is_device_reload_pending{true};
- std::string udp_input_address;
- u16 udp_input_port;
- u8 udp_pad_index;
- bool use_docked_mode;
+ bool use_touch_from_button;
+ std::string touch_device;
+ int touch_from_button_map_index;
+ std::vector<TouchFromButtonMap> touch_from_button_maps;
+
+ std::atomic_bool is_device_reload_pending{true};
// Data Storage
bool use_virtual_sd;
@@ -516,13 +233,18 @@ struct Values {
// Add-Ons
std::map<u64, std::vector<std::string>> disabled_addons;
-} extern values;
+};
-float Volume();
+extern Values values;
+
+bool IsConfiguringGlobal();
+void SetConfiguringGlobal(bool is_global);
bool IsGPULevelExtreme();
bool IsGPULevelHigh();
+float Volume();
+
std::string GetTimeZoneString();
void Apply();
diff --git a/src/core/telemetry_session.cpp b/src/core/telemetry_session.cpp
index da09c0dbc..d11b15f38 100644
--- a/src/core/telemetry_session.cpp
+++ b/src/core/telemetry_session.cpp
@@ -147,7 +147,9 @@ TelemetrySession::~TelemetrySession() {
}
}
-void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
+void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader,
+ const Service::FileSystem::FileSystemController& fsc,
+ const FileSys::ContentProvider& content_provider) {
// Log one-time top-level information
AddField(Telemetry::FieldType::None, "TelemetryId", GetTelemetryId());
@@ -167,7 +169,10 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
app_loader.ReadTitle(name);
if (name.empty()) {
- const auto metadata = FileSys::PatchManager(program_id).GetControlMetadata();
+ const auto metadata = [&content_provider, &fsc, program_id] {
+ const FileSys::PatchManager pm{program_id, fsc, content_provider};
+ return pm.GetControlMetadata();
+ }();
if (metadata.first != nullptr) {
name = metadata.first->GetApplicationName();
}
@@ -206,12 +211,14 @@ void TelemetrySession::AddInitialInfo(Loader::AppLoader& app_loader) {
TranslateGPUAccuracyLevel(Settings::values.gpu_accuracy.GetValue()));
AddField(field_type, "Renderer_UseAsynchronousGpuEmulation",
Settings::values.use_asynchronous_gpu_emulation.GetValue());
+ AddField(field_type, "Renderer_UseNvdecEmulation",
+ Settings::values.use_nvdec_emulation.GetValue());
AddField(field_type, "Renderer_UseVsync", Settings::values.use_vsync.GetValue());
AddField(field_type, "Renderer_UseAssemblyShaders",
Settings::values.use_assembly_shaders.GetValue());
AddField(field_type, "Renderer_UseAsynchronousShaders",
Settings::values.use_asynchronous_shaders.GetValue());
- AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode);
+ AddField(field_type, "System_UseDockedMode", Settings::values.use_docked_mode.GetValue());
}
bool 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 @@
#include <string>
#include "common/telemetry.h"
+namespace FileSys {
+class ContentProvider;
+}
+
namespace Loader {
class AppLoader;
}
+namespace Service::FileSystem {
+class FileSystemController;
+}
+
namespace Core {
/**
@@ -40,10 +48,14 @@ public:
* - Title file format
* - Miscellaneous settings values.
*
- * @param app_loader The application loader to use to retrieve
- * title-specific information.
+ * @param app_loader The application loader to use to retrieve
+ * title-specific information.
+ * @param fsc Filesystem controller to use to retrieve info.
+ * @param content_provider Content provider to use to retrieve info.
*/
- void AddInitialInfo(Loader::AppLoader& app_loader);
+ void AddInitialInfo(Loader::AppLoader& app_loader,
+ const Service::FileSystem::FileSystemController& fsc,
+ const FileSys::ContentProvider& content_provider);
/**
* Wrapper around the Telemetry::FieldCollection::AddField method.
diff --git a/src/input_common/CMakeLists.txt b/src/input_common/CMakeLists.txt
index 317c25bad..7b39a38c1 100644
--- a/src/input_common/CMakeLists.txt
+++ b/src/input_common/CMakeLists.txt
@@ -7,6 +7,14 @@ add_library(input_common STATIC
main.h
motion_emu.cpp
motion_emu.h
+ motion_from_button.cpp
+ motion_from_button.h
+ motion_input.cpp
+ motion_input.h
+ settings.cpp
+ settings.h
+ touch_from_button.cpp
+ touch_from_button.h
gcadapter/gc_adapter.cpp
gcadapter/gc_adapter.h
gcadapter/gc_poller.cpp
@@ -21,6 +29,35 @@ add_library(input_common STATIC
udp/udp.h
)
+if (MSVC)
+ target_compile_options(input_common PRIVATE
+ # 'expression' : signed/unsigned mismatch
+ /we4018
+ # 'argument' : conversion from 'type1' to 'type2', possible loss of data (floating-point)
+ /we4244
+ # 'conversion' : conversion from 'type1' to 'type2', signed/unsigned mismatch
+ /we4245
+ # 'operator': conversion from 'type1:field_bits' to 'type2:field_bits', possible loss of data
+ /we4254
+ # 'var' : conversion from 'size_t' to 'type', possible loss of data
+ /we4267
+ # 'context' : truncation from 'type1' to 'type2'
+ /we4305
+ )
+else()
+ target_compile_options(input_common PRIVATE
+ -Werror=conversion
+ -Werror=ignored-qualifiers
+ -Werror=implicit-fallthrough
+ -Werror=reorder
+ -Werror=shadow
+ -Werror=sign-compare
+ -Werror=unused-but-set-parameter
+ -Werror=unused-but-set-variable
+ -Werror=unused-variable
+ )
+endif()
+
if(SDL2_FOUND)
target_sources(input_common PRIVATE
sdl/sdl_impl.cpp
diff --git a/src/input_common/analog_from_button.cpp b/src/input_common/analog_from_button.cpp
index 6cabdaa3c..74744d7f3 100755
--- a/src/input_common/analog_from_button.cpp
+++ b/src/input_common/analog_from_button.cpp
@@ -20,18 +20,22 @@ public:
constexpr float SQRT_HALF = 0.707106781f;
int x = 0, y = 0;
- if (right->GetStatus())
+ if (right->GetStatus()) {
++x;
- if (left->GetStatus())
+ }
+ if (left->GetStatus()) {
--x;
- if (up->GetStatus())
+ }
+ if (up->GetStatus()) {
++y;
- if (down->GetStatus())
+ }
+ if (down->GetStatus()) {
--y;
+ }
- float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
- return std::make_tuple(x * coef * (y == 0 ? 1.0f : SQRT_HALF),
- y * coef * (x == 0 ? 1.0f : SQRT_HALF));
+ const float coef = modifier->GetStatus() ? modifier_scale : 1.0f;
+ return std::make_tuple(static_cast<float>(x) * coef * (y == 0 ? 1.0f : SQRT_HALF),
+ static_cast<float>(y) * coef * (x == 0 ? 1.0f : SQRT_HALF));
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
diff --git a/src/input_common/gcadapter/gc_adapter.cpp b/src/input_common/gcadapter/gc_adapter.cpp
index 74759ea7d..d80195c82 100644
--- a/src/input_common/gcadapter/gc_adapter.cpp
+++ b/src/input_common/gcadapter/gc_adapter.cpp
@@ -4,20 +4,23 @@
#include <chrono>
#include <thread>
+
+#ifdef _MSC_VER
+#pragma warning(push)
+#pragma warning(disable : 4200) // nonstandard extension used : zero-sized array in struct/union
+#endif
#include <libusb.h>
+#ifdef _MSC_VER
+#pragma warning(pop)
+#endif
+
#include "common/logging/log.h"
+#include "common/param_package.h"
#include "input_common/gcadapter/gc_adapter.h"
+#include "input_common/settings.h"
namespace GCAdapter {
-/// Used to loop through and assign button in poller
-constexpr std::array<PadButton, 12> PadButtonArray{
- PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT, PadButton::PAD_BUTTON_DOWN,
- PadButton::PAD_BUTTON_UP, PadButton::PAD_TRIGGER_Z, PadButton::PAD_TRIGGER_R,
- PadButton::PAD_TRIGGER_L, PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B,
- PadButton::PAD_BUTTON_X, PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_START,
-};
-
Adapter::Adapter() {
if (usb_adapter_handle != nullptr) {
return;
@@ -26,179 +29,261 @@ Adapter::Adapter() {
const int init_res = libusb_init(&libusb_ctx);
if (init_res == LIBUSB_SUCCESS) {
- Setup();
+ adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
} else {
LOG_ERROR(Input, "libusb could not be initialized. failed with error = {}", init_res);
}
}
-GCPadStatus Adapter::GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload) {
- GCPadStatus pad = {};
- const std::size_t offset = 1 + (9 * port);
+Adapter::~Adapter() {
+ Reset();
+}
- adapter_controllers_status[port] = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
+void Adapter::AdapterInputThread() {
+ LOG_DEBUG(Input, "GC Adapter input thread started");
+ s32 payload_size{};
+ AdapterPayload adapter_payload{};
- static constexpr std::array<PadButton, 8> b1_buttons{
- PadButton::PAD_BUTTON_A, PadButton::PAD_BUTTON_B, PadButton::PAD_BUTTON_X,
- PadButton::PAD_BUTTON_Y, PadButton::PAD_BUTTON_LEFT, PadButton::PAD_BUTTON_RIGHT,
- PadButton::PAD_BUTTON_DOWN, PadButton::PAD_BUTTON_UP,
- };
+ if (adapter_scan_thread.joinable()) {
+ adapter_scan_thread.join();
+ }
- static constexpr std::array<PadButton, 4> b2_buttons{
- PadButton::PAD_BUTTON_START,
- PadButton::PAD_TRIGGER_Z,
- PadButton::PAD_TRIGGER_R,
- PadButton::PAD_TRIGGER_L,
- };
+ while (adapter_input_thread_running) {
+ libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
+ static_cast<s32>(adapter_payload.size()), &payload_size, 16);
+ if (IsPayloadCorrect(adapter_payload, payload_size)) {
+ UpdateControllers(adapter_payload);
+ UpdateVibrations();
+ }
+ std::this_thread::yield();
+ }
- static constexpr std::array<PadAxes, 6> axes{
- PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
- PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
- };
+ if (restart_scan_thread) {
+ adapter_scan_thread = std::thread(&Adapter::AdapterScanThread, this);
+ restart_scan_thread = false;
+ }
+}
- if (adapter_controllers_status[port] == ControllerTypes::None && !get_origin[port]) {
- // Controller may have been disconnected, recalibrate if reconnected.
- get_origin[port] = true;
+bool Adapter::IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size) {
+ if (payload_size != static_cast<s32>(adapter_payload.size()) ||
+ adapter_payload[0] != LIBUSB_DT_HID) {
+ LOG_DEBUG(Input, "Error reading payload (size: {}, type: {:02x})", payload_size,
+ adapter_payload[0]);
+ if (input_error_counter++ > 20) {
+ LOG_ERROR(Input, "GC adapter timeout, Is the adapter connected?");
+ adapter_input_thread_running = false;
+ restart_scan_thread = true;
+ }
+ return false;
}
- if (adapter_controllers_status[port] != ControllerTypes::None) {
- const u8 b1 = adapter_payload[offset + 1];
- const u8 b2 = adapter_payload[offset + 2];
+ input_error_counter = 0;
+ return true;
+}
- for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
- if ((b1 & (1U << i)) != 0) {
- pad.button |= static_cast<u16>(b1_buttons[i]);
+void Adapter::UpdateControllers(const AdapterPayload& adapter_payload) {
+ for (std::size_t port = 0; port < pads.size(); ++port) {
+ const std::size_t offset = 1 + (9 * port);
+ const auto type = static_cast<ControllerTypes>(adapter_payload[offset] >> 4);
+ UpdatePadType(port, type);
+ if (DeviceConnected(port)) {
+ const u8 b1 = adapter_payload[offset + 1];
+ const u8 b2 = adapter_payload[offset + 2];
+ UpdateStateButtons(port, b1, b2);
+ UpdateStateAxes(port, adapter_payload);
+ if (configuring) {
+ UpdateYuzuSettings(port);
}
}
+ }
+}
- for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
- if ((b2 & (1U << j)) != 0) {
- pad.button |= static_cast<u16>(b2_buttons[j]);
- }
- }
- for (PadAxes axis : axes) {
- const std::size_t index = static_cast<std::size_t>(axis);
- pad.axis_values[index] = adapter_payload[offset + 3 + index];
+void Adapter::UpdatePadType(std::size_t port, ControllerTypes pad_type) {
+ if (pads[port].type == pad_type) {
+ return;
+ }
+ // Device changed reset device and set new type
+ ResetDevice(port);
+ pads[port].type = pad_type;
+}
+
+void Adapter::UpdateStateButtons(std::size_t port, u8 b1, u8 b2) {
+ if (port >= pads.size()) {
+ return;
+ }
+
+ static constexpr std::array<PadButton, 8> b1_buttons{
+ PadButton::ButtonA, PadButton::ButtonB, PadButton::ButtonX, PadButton::ButtonY,
+ PadButton::ButtonLeft, PadButton::ButtonRight, PadButton::ButtonDown, PadButton::ButtonUp,
+ };
+
+ static constexpr std::array<PadButton, 4> b2_buttons{
+ PadButton::ButtonStart,
+ PadButton::TriggerZ,
+ PadButton::TriggerR,
+ PadButton::TriggerL,
+ };
+ pads[port].buttons = 0;
+ for (std::size_t i = 0; i < b1_buttons.size(); ++i) {
+ if ((b1 & (1U << i)) != 0) {
+ pads[port].buttons =
+ static_cast<u16>(pads[port].buttons | static_cast<u16>(b1_buttons[i]));
+ pads[port].last_button = b1_buttons[i];
}
+ }
- if (get_origin[port]) {
- origin_status[port].axis_values = pad.axis_values;
- get_origin[port] = false;
+ for (std::size_t j = 0; j < b2_buttons.size(); ++j) {
+ if ((b2 & (1U << j)) != 0) {
+ pads[port].buttons =
+ static_cast<u16>(pads[port].buttons | static_cast<u16>(b2_buttons[j]));
+ pads[port].last_button = b2_buttons[j];
}
}
- return pad;
}
-void Adapter::PadToState(const GCPadStatus& pad, GCState& state) {
- for (const auto& button : PadButtonArray) {
- const u16 button_value = static_cast<u16>(button);
- state.buttons.insert_or_assign(button_value, pad.button & button_value);
+void Adapter::UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload) {
+ if (port >= pads.size()) {
+ return;
}
- for (size_t i = 0; i < pad.axis_values.size(); ++i) {
- state.axes.insert_or_assign(static_cast<u8>(i), pad.axis_values[i]);
+ const std::size_t offset = 1 + (9 * port);
+ static constexpr std::array<PadAxes, 6> axes{
+ PadAxes::StickX, PadAxes::StickY, PadAxes::SubstickX,
+ PadAxes::SubstickY, PadAxes::TriggerLeft, PadAxes::TriggerRight,
+ };
+
+ for (const PadAxes axis : axes) {
+ const auto index = static_cast<std::size_t>(axis);
+ const u8 axis_value = adapter_payload[offset + 3 + index];
+ if (pads[port].axis_origin[index] == 255) {
+ pads[port].axis_origin[index] = axis_value;
+ }
+ pads[port].axis_values[index] =
+ static_cast<s16>(axis_value - pads[port].axis_origin[index]);
}
}
-void Adapter::Read() {
- LOG_DEBUG(Input, "GC Adapter Read() thread started");
+void Adapter::UpdateYuzuSettings(std::size_t port) {
+ if (port >= pads.size()) {
+ return;
+ }
- int payload_size;
- std::array<u8, 37> adapter_payload;
- std::array<GCPadStatus, 4> pads;
+ constexpr u8 axis_threshold = 50;
+ GCPadStatus pad_status = {.port = port};
- while (adapter_thread_running) {
- libusb_interrupt_transfer(usb_adapter_handle, input_endpoint, adapter_payload.data(),
- sizeof(adapter_payload), &payload_size, 16);
-
- if (payload_size != sizeof(adapter_payload) || adapter_payload[0] != LIBUSB_DT_HID) {
- LOG_ERROR(Input,
- "Error reading payload (size: {}, type: {:02x}) Is the adapter connected?",
- payload_size, adapter_payload[0]);
- adapter_thread_running = false; // error reading from adapter, stop reading.
- break;
- }
- for (std::size_t port = 0; port < pads.size(); ++port) {
- pads[port] = GetPadStatus(port, adapter_payload);
- if (DeviceConnected(port) && configuring) {
- if (pads[port].button != 0) {
- pad_queue[port].Push(pads[port]);
- }
+ if (pads[port].buttons != 0) {
+ pad_status.button = pads[port].last_button;
+ pad_queue.Push(pad_status);
+ }
- // Accounting for a threshold here to ensure an intentional press
- for (size_t i = 0; i < pads[port].axis_values.size(); ++i) {
- const u8 value = pads[port].axis_values[i];
- const u8 origin = origin_status[port].axis_values[i];
-
- if (value > origin + pads[port].THRESHOLD ||
- value < origin - pads[port].THRESHOLD) {
- pads[port].axis = static_cast<PadAxes>(i);
- pads[port].axis_value = pads[port].axis_values[i];
- pad_queue[port].Push(pads[port]);
- }
- }
- }
- PadToState(pads[port], state[port]);
+ // Accounting for a threshold here to ensure an intentional press
+ for (std::size_t i = 0; i < pads[port].axis_values.size(); ++i) {
+ const s16 value = pads[port].axis_values[i];
+
+ if (value > axis_threshold || value < -axis_threshold) {
+ pad_status.axis = static_cast<PadAxes>(i);
+ pad_status.axis_value = value;
+ pad_status.axis_threshold = axis_threshold;
+ pad_queue.Push(pad_status);
}
- std::this_thread::yield();
}
}
-void Adapter::Setup() {
- // Initialize all controllers as unplugged
- adapter_controllers_status.fill(ControllerTypes::None);
- // Initialize all ports to store axis origin values
- get_origin.fill(true);
-
- // pointer to list of connected usb devices
- libusb_device** devices{};
-
- // populate the list of devices, get the count
- const ssize_t device_count = libusb_get_device_list(libusb_ctx, &devices);
- if (device_count < 0) {
- LOG_ERROR(Input, "libusb_get_device_list failed with error: {}", device_count);
- return;
+void Adapter::UpdateVibrations() {
+ // Use 8 states to keep the switching between on/off fast enough for
+ // a human to not notice the difference between switching from on/off
+ // More states = more rumble strengths = slower update time
+ constexpr u8 vibration_states = 8;
+
+ vibration_counter = (vibration_counter + 1) % vibration_states;
+
+ for (GCController& pad : pads) {
+ const bool vibrate = pad.rumble_amplitude > vibration_counter;
+ vibration_changed |= vibrate != pad.enable_vibration;
+ pad.enable_vibration = vibrate;
}
+ SendVibrations();
+}
- if (devices != nullptr) {
- for (std::size_t index = 0; index < static_cast<std::size_t>(device_count); ++index) {
- if (CheckDeviceAccess(devices[index])) {
- // GC Adapter found and accessible, registering it
- GetGCEndpoint(devices[index]);
- break;
- }
+void Adapter::SendVibrations() {
+ if (!rumble_enabled || !vibration_changed) {
+ return;
+ }
+ s32 size{};
+ constexpr u8 rumble_command = 0x11;
+ const u8 p1 = pads[0].enable_vibration;
+ const u8 p2 = pads[1].enable_vibration;
+ const u8 p3 = pads[2].enable_vibration;
+ const u8 p4 = pads[3].enable_vibration;
+ std::array<u8, 5> payload = {rumble_command, p1, p2, p3, p4};
+ const int err = libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, payload.data(),
+ static_cast<s32>(payload.size()), &size, 16);
+ if (err) {
+ LOG_DEBUG(Input, "Adapter libusb write failed: {}", libusb_error_name(err));
+ if (output_error_counter++ > 5) {
+ LOG_ERROR(Input, "GC adapter output timeout, Rumble disabled");
+ rumble_enabled = false;
}
- libusb_free_device_list(devices, 1);
+ return;
}
+ output_error_counter = 0;
+ vibration_changed = false;
}
-bool Adapter::CheckDeviceAccess(libusb_device* device) {
- libusb_device_descriptor desc;
- const int get_descriptor_error = libusb_get_device_descriptor(device, &desc);
- if (get_descriptor_error) {
- // could not acquire the descriptor, no point in trying to use it.
- LOG_ERROR(Input, "libusb_get_device_descriptor failed with error: {}",
- get_descriptor_error);
- return false;
+bool Adapter::RumblePlay(std::size_t port, u8 amplitude) {
+ pads[port].rumble_amplitude = amplitude;
+
+ return rumble_enabled;
+}
+
+void Adapter::AdapterScanThread() {
+ adapter_scan_thread_running = true;
+ adapter_input_thread_running = false;
+ if (adapter_input_thread.joinable()) {
+ adapter_input_thread.join();
+ }
+ ClearLibusbHandle();
+ ResetDevices();
+ while (adapter_scan_thread_running && !adapter_input_thread_running) {
+ Setup();
+ std::this_thread::sleep_for(std::chrono::seconds(1));
}
+}
- if (desc.idVendor != 0x057e || desc.idProduct != 0x0337) {
- // This isn't the device we are looking for.
- return false;
+void Adapter::Setup() {
+ usb_adapter_handle = libusb_open_device_with_vid_pid(libusb_ctx, 0x057e, 0x0337);
+
+ if (usb_adapter_handle == NULL) {
+ return;
+ }
+ if (!CheckDeviceAccess()) {
+ ClearLibusbHandle();
+ return;
}
- const int open_error = libusb_open(device, &usb_adapter_handle);
- if (open_error == LIBUSB_ERROR_ACCESS) {
- LOG_ERROR(Input, "Yuzu can not gain access to this device: ID {:04X}:{:04X}.",
- desc.idVendor, desc.idProduct);
- return false;
+ libusb_device* device = libusb_get_device(usb_adapter_handle);
+
+ LOG_INFO(Input, "GC adapter is now connected");
+ // GC Adapter found and accessible, registering it
+ if (GetGCEndpoint(device)) {
+ adapter_scan_thread_running = false;
+ adapter_input_thread_running = true;
+ rumble_enabled = true;
+ input_error_counter = 0;
+ output_error_counter = 0;
+ adapter_input_thread = std::thread(&Adapter::AdapterInputThread, this);
}
- if (open_error) {
- LOG_ERROR(Input, "libusb_open failed to open device with error = {}", open_error);
- return false;
+}
+
+bool Adapter::CheckDeviceAccess() {
+ // This fixes payload problems from offbrand GCAdapters
+ const s32 control_transfer_error =
+ libusb_control_transfer(usb_adapter_handle, 0x21, 11, 0x0001, 0, nullptr, 0, 1000);
+ if (control_transfer_error < 0) {
+ LOG_ERROR(Input, "libusb_control_transfer failed with error= {}", control_transfer_error);
}
- int kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
+ s32 kernel_driver_error = libusb_kernel_driver_active(usb_adapter_handle, 0);
if (kernel_driver_error == 1) {
kernel_driver_error = libusb_detach_kernel_driver(usb_adapter_handle, 0);
if (kernel_driver_error != 0 && kernel_driver_error != LIBUSB_ERROR_NOT_SUPPORTED) {
@@ -224,13 +309,13 @@ bool Adapter::CheckDeviceAccess(libusb_device* device) {
return true;
}
-void Adapter::GetGCEndpoint(libusb_device* device) {
+bool Adapter::GetGCEndpoint(libusb_device* device) {
libusb_config_descriptor* config = nullptr;
const int config_descriptor_return = libusb_get_config_descriptor(device, 0, &config);
if (config_descriptor_return != LIBUSB_SUCCESS) {
LOG_ERROR(Input, "libusb_get_config_descriptor failed with error = {}",
config_descriptor_return);
- return;
+ return false;
}
for (u8 ic = 0; ic < config->bNumInterfaces; ic++) {
@@ -239,7 +324,7 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
const libusb_interface_descriptor* interface = &interfaceContainer->altsetting[i];
for (u8 e = 0; e < interface->bNumEndpoints; e++) {
const libusb_endpoint_descriptor* endpoint = &interface->endpoint[e];
- if (endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) {
+ if ((endpoint->bEndpointAddress & LIBUSB_ENDPOINT_IN) != 0) {
input_endpoint = endpoint->bEndpointAddress;
} else {
output_endpoint = endpoint->bEndpointAddress;
@@ -252,78 +337,173 @@ void Adapter::GetGCEndpoint(libusb_device* device) {
unsigned char clear_payload = 0x13;
libusb_interrupt_transfer(usb_adapter_handle, output_endpoint, &clear_payload,
sizeof(clear_payload), nullptr, 16);
-
- adapter_thread_running = true;
- adapter_input_thread = std::thread(&Adapter::Read, this);
+ return true;
}
-Adapter::~Adapter() {
- Reset();
-}
+void Adapter::JoinThreads() {
+ restart_scan_thread = false;
+ adapter_input_thread_running = false;
+ adapter_scan_thread_running = false;
-void Adapter::Reset() {
- if (adapter_thread_running) {
- adapter_thread_running = false;
+ if (adapter_scan_thread.joinable()) {
+ adapter_scan_thread.join();
}
+
if (adapter_input_thread.joinable()) {
adapter_input_thread.join();
}
+}
- adapter_controllers_status.fill(ControllerTypes::None);
- get_origin.fill(true);
-
+void Adapter::ClearLibusbHandle() {
if (usb_adapter_handle) {
libusb_release_interface(usb_adapter_handle, 1);
libusb_close(usb_adapter_handle);
usb_adapter_handle = nullptr;
}
+}
+
+void Adapter::ResetDevices() {
+ for (std::size_t i = 0; i < pads.size(); ++i) {
+ ResetDevice(i);
+ }
+}
+
+void Adapter::ResetDevice(std::size_t port) {
+ pads[port].type = ControllerTypes::None;
+ pads[port].enable_vibration = false;
+ pads[port].rumble_amplitude = 0;
+ pads[port].buttons = 0;
+ pads[port].last_button = PadButton::Undefined;
+ pads[port].axis_values.fill(0);
+ pads[port].axis_origin.fill(255);
+}
+
+void Adapter::Reset() {
+ JoinThreads();
+ ClearLibusbHandle();
+ ResetDevices();
if (libusb_ctx) {
libusb_exit(libusb_ctx);
}
}
-bool Adapter::DeviceConnected(std::size_t port) {
- return adapter_controllers_status[port] != ControllerTypes::None;
+std::vector<Common::ParamPackage> Adapter::GetInputDevices() const {
+ std::vector<Common::ParamPackage> devices;
+ for (std::size_t port = 0; port < pads.size(); ++port) {
+ if (!DeviceConnected(port)) {
+ continue;
+ }
+ std::string name = fmt::format("Gamecube Controller {}", port + 1);
+ devices.emplace_back(Common::ParamPackage{
+ {"class", "gcpad"},
+ {"display", std::move(name)},
+ {"port", std::to_string(port)},
+ });
+ }
+ return devices;
}
-void Adapter::ResetDeviceType(std::size_t port) {
- adapter_controllers_status[port] = ControllerTypes::None;
+InputCommon::ButtonMapping Adapter::GetButtonMappingForDevice(
+ const Common::ParamPackage& params) const {
+ // This list is missing ZL/ZR since those are not considered buttons.
+ // We will add those afterwards
+ // This list also excludes any button that can't be really mapped
+ static constexpr std::array<std::pair<Settings::NativeButton::Values, PadButton>, 12>
+ switch_to_gcadapter_button = {
+ std::pair{Settings::NativeButton::A, PadButton::ButtonA},
+ {Settings::NativeButton::B, PadButton::ButtonB},
+ {Settings::NativeButton::X, PadButton::ButtonX},
+ {Settings::NativeButton::Y, PadButton::ButtonY},
+ {Settings::NativeButton::Plus, PadButton::ButtonStart},
+ {Settings::NativeButton::DLeft, PadButton::ButtonLeft},
+ {Settings::NativeButton::DUp, PadButton::ButtonUp},
+ {Settings::NativeButton::DRight, PadButton::ButtonRight},
+ {Settings::NativeButton::DDown, PadButton::ButtonDown},
+ {Settings::NativeButton::SL, PadButton::TriggerL},
+ {Settings::NativeButton::SR, PadButton::TriggerR},
+ {Settings::NativeButton::R, PadButton::TriggerZ},
+ };
+ if (!params.Has("port")) {
+ return {};
+ }
+
+ InputCommon::ButtonMapping mapping{};
+ for (const auto& [switch_button, gcadapter_button] : switch_to_gcadapter_button) {
+ Common::ParamPackage button_params({{"engine", "gcpad"}});
+ button_params.Set("port", params.Get("port", 0));
+ button_params.Set("button", static_cast<int>(gcadapter_button));
+ mapping.insert_or_assign(switch_button, std::move(button_params));
+ }
+
+ // Add the missing bindings for ZL/ZR
+ static constexpr std::array<std::pair<Settings::NativeButton::Values, PadAxes>, 2>
+ switch_to_gcadapter_axis = {
+ std::pair{Settings::NativeButton::ZL, PadAxes::TriggerLeft},
+ {Settings::NativeButton::ZR, PadAxes::TriggerRight},
+ };
+ for (const auto& [switch_button, gcadapter_axis] : switch_to_gcadapter_axis) {
+ Common::ParamPackage button_params({{"engine", "gcpad"}});
+ button_params.Set("port", params.Get("port", 0));
+ button_params.Set("button", static_cast<s32>(PadButton::Stick));
+ button_params.Set("axis", static_cast<s32>(gcadapter_axis));
+ button_params.Set("threshold", 0.5f);
+ button_params.Set("direction", "+");
+ mapping.insert_or_assign(switch_button, std::move(button_params));
+ }
+ return mapping;
}
-void Adapter::BeginConfiguration() {
- get_origin.fill(true);
- for (auto& pq : pad_queue) {
- pq.Clear();
+InputCommon::AnalogMapping Adapter::GetAnalogMappingForDevice(
+ const Common::ParamPackage& params) const {
+ if (!params.Has("port")) {
+ return {};
}
+
+ InputCommon::AnalogMapping mapping = {};
+ Common::ParamPackage left_analog_params;
+ left_analog_params.Set("engine", "gcpad");
+ left_analog_params.Set("port", params.Get("port", 0));
+ left_analog_params.Set("axis_x", static_cast<int>(PadAxes::StickX));
+ left_analog_params.Set("axis_y", static_cast<int>(PadAxes::StickY));
+ mapping.insert_or_assign(Settings::NativeAnalog::LStick, std::move(left_analog_params));
+ Common::ParamPackage right_analog_params;
+ right_analog_params.Set("engine", "gcpad");
+ right_analog_params.Set("port", params.Get("port", 0));
+ right_analog_params.Set("axis_x", static_cast<int>(PadAxes::SubstickX));
+ right_analog_params.Set("axis_y", static_cast<int>(PadAxes::SubstickY));
+ mapping.insert_or_assign(Settings::NativeAnalog::RStick, std::move(right_analog_params));
+ return mapping;
+}
+
+bool Adapter::DeviceConnected(std::size_t port) const {
+ return pads[port].type != ControllerTypes::None;
+}
+
+void Adapter::BeginConfiguration() {
+ pad_queue.Clear();
configuring = true;
}
void Adapter::EndConfiguration() {
- for (auto& pq : pad_queue) {
- pq.Clear();
- }
+ pad_queue.Clear();
configuring = false;
}
-std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() {
+Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() {
return pad_queue;
}
-const std::array<Common::SPSCQueue<GCPadStatus>, 4>& Adapter::GetPadQueue() const {
+const Common::SPSCQueue<GCPadStatus>& Adapter::GetPadQueue() const {
return pad_queue;
}
-std::array<GCState, 4>& Adapter::GetPadState() {
- return state;
-}
-
-const std::array<GCState, 4>& Adapter::GetPadState() const {
- return state;
+GCController& Adapter::GetPadState(std::size_t port) {
+ return pads.at(port);
}
-int Adapter::GetOriginValue(int port, int axis) const {
- return origin_status[port].axis_values[axis];
+const GCController& Adapter::GetPadState(std::size_t port) const {
+ return pads.at(port);
}
} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_adapter.h b/src/input_common/gcadapter/gc_adapter.h
index bed81915c..f1256c9da 100644
--- a/src/input_common/gcadapter/gc_adapter.h
+++ b/src/input_common/gcadapter/gc_adapter.h
@@ -10,6 +10,7 @@
#include <unordered_map>
#include "common/common_types.h"
#include "common/threadsafe_queue.h"
+#include "input_common/main.h"
struct libusb_context;
struct libusb_device;
@@ -18,24 +19,23 @@ struct libusb_device_handle;
namespace GCAdapter {
enum class PadButton {
- PAD_BUTTON_LEFT = 0x0001,
- PAD_BUTTON_RIGHT = 0x0002,
- PAD_BUTTON_DOWN = 0x0004,
- PAD_BUTTON_UP = 0x0008,
- PAD_TRIGGER_Z = 0x0010,
- PAD_TRIGGER_R = 0x0020,
- PAD_TRIGGER_L = 0x0040,
- PAD_BUTTON_A = 0x0100,
- PAD_BUTTON_B = 0x0200,
- PAD_BUTTON_X = 0x0400,
- PAD_BUTTON_Y = 0x0800,
- PAD_BUTTON_START = 0x1000,
+ Undefined = 0x0000,
+ ButtonLeft = 0x0001,
+ ButtonRight = 0x0002,
+ ButtonDown = 0x0004,
+ ButtonUp = 0x0008,
+ TriggerZ = 0x0010,
+ TriggerR = 0x0020,
+ TriggerL = 0x0040,
+ ButtonA = 0x0100,
+ ButtonB = 0x0200,
+ ButtonX = 0x0400,
+ ButtonY = 0x0800,
+ ButtonStart = 0x1000,
// Below is for compatibility with "AxisButton" type
- PAD_STICK = 0x2000,
+ Stick = 0x2000,
};
-extern const std::array<PadButton, 12> PadButtonArray;
-
enum class PadAxes : u8 {
StickX,
StickY,
@@ -46,85 +46,122 @@ enum class PadAxes : u8 {
Undefined,
};
+enum class ControllerTypes {
+ None,
+ Wired,
+ Wireless,
+};
+
struct GCPadStatus {
- u16 button{}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
+ std::size_t port{};
- std::array<u8, 6> axis_values{}; // Triggers and sticks, following indices defined in PadAxes
- static constexpr u8 THRESHOLD = 50; // Threshold for axis press for polling
+ PadButton button{PadButton::Undefined}; // Or-ed PAD_BUTTON_* and PAD_TRIGGER_* bits
- u8 port{};
PadAxes axis{PadAxes::Undefined};
- u8 axis_value{255};
+ s16 axis_value{};
+ u8 axis_threshold{50};
};
-struct GCState {
- std::unordered_map<int, bool> buttons;
- std::unordered_map<int, u16> axes;
+struct GCController {
+ ControllerTypes type{};
+ bool enable_vibration{};
+ u8 rumble_amplitude{};
+ u16 buttons{};
+ PadButton last_button{};
+ std::array<s16, 6> axis_values{};
+ std::array<u8, 6> axis_origin{};
};
-enum class ControllerTypes { None, Wired, Wireless };
-
class Adapter {
public:
- /// Initialize the GC Adapter capture and read sequence
Adapter();
-
- /// Close the adapter read thread and release the adapter
~Adapter();
+
+ /// Request a vibration for a controller
+ bool RumblePlay(std::size_t port, u8 amplitude);
+
/// Used for polling
void BeginConfiguration();
void EndConfiguration();
- /// Returns true if there is a device connected to port
- bool DeviceConnected(std::size_t port);
+ Common::SPSCQueue<GCPadStatus>& GetPadQueue();
+ const Common::SPSCQueue<GCPadStatus>& GetPadQueue() const;
- std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue();
- const std::array<Common::SPSCQueue<GCPadStatus>, 4>& GetPadQueue() const;
+ GCController& GetPadState(std::size_t port);
+ const GCController& GetPadState(std::size_t port) const;
- std::array<GCState, 4>& GetPadState();
- const std::array<GCState, 4>& GetPadState() const;
+ /// Returns true if there is a device connected to port
+ bool DeviceConnected(std::size_t port) const;
- int GetOriginValue(int port, int axis) const;
+ /// Used for automapping features
+ std::vector<Common::ParamPackage> GetInputDevices() const;
+ InputCommon::ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) const;
+ InputCommon::AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) const;
private:
- GCPadStatus GetPadStatus(std::size_t port, const std::array<u8, 37>& adapter_payload);
+ using AdapterPayload = std::array<u8, 37>;
+
+ void UpdatePadType(std::size_t port, ControllerTypes pad_type);
+ void UpdateControllers(const AdapterPayload& adapter_payload);
+ void UpdateYuzuSettings(std::size_t port);
+ void UpdateStateButtons(std::size_t port, u8 b1, u8 b2);
+ void UpdateStateAxes(std::size_t port, const AdapterPayload& adapter_payload);
+ void UpdateVibrations();
+
+ void AdapterInputThread();
- void PadToState(const GCPadStatus& pad, GCState& state);
+ void AdapterScanThread();
- void Read();
+ bool IsPayloadCorrect(const AdapterPayload& adapter_payload, s32 payload_size);
- /// Resets status of device connected to port
- void ResetDeviceType(std::size_t port);
+ // Updates vibration state of all controllers
+ void SendVibrations();
+
+ /// For use in initialization, querying devices to find the adapter
+ void Setup();
+
+ /// Resets status of all GC controller devices to a disconected state
+ void ResetDevices();
+
+ /// Resets status of device connected to a disconected state
+ void ResetDevice(std::size_t port);
/// Returns true if we successfully gain access to GC Adapter
- bool CheckDeviceAccess(libusb_device* device);
+ bool CheckDeviceAccess();
- /// Captures GC Adapter endpoint address,
- void GetGCEndpoint(libusb_device* device);
+ /// Captures GC Adapter endpoint address
+ /// Returns true if the endpoind was set correctly
+ bool GetGCEndpoint(libusb_device* device);
/// For shutting down, clear all data, join all threads, release usb
void Reset();
- /// For use in initialization, querying devices to find the adapter
- void Setup();
+ // Join all threads
+ void JoinThreads();
+
+ // Release usb handles
+ void ClearLibusbHandle();
libusb_device_handle* usb_adapter_handle = nullptr;
+ std::array<GCController, 4> pads;
+ Common::SPSCQueue<GCPadStatus> pad_queue;
std::thread adapter_input_thread;
- bool adapter_thread_running;
+ std::thread adapter_scan_thread;
+ bool adapter_input_thread_running;
+ bool adapter_scan_thread_running;
+ bool restart_scan_thread;
libusb_context* libusb_ctx;
- u8 input_endpoint = 0;
- u8 output_endpoint = 0;
+ u8 input_endpoint{0};
+ u8 output_endpoint{0};
+ u8 input_error_counter{0};
+ u8 output_error_counter{0};
+ int vibration_counter{0};
- bool configuring = false;
-
- std::array<GCState, 4> state;
- std::array<bool, 4> get_origin;
- std::array<GCPadStatus, 4> origin_status;
- std::array<Common::SPSCQueue<GCPadStatus>, 4> pad_queue;
- std::array<ControllerTypes, 4> adapter_controllers_status{};
+ bool configuring{false};
+ bool rumble_enabled{true};
+ bool vibration_changed{true};
};
-
} // namespace GCAdapter
diff --git a/src/input_common/gcadapter/gc_poller.cpp b/src/input_common/gcadapter/gc_poller.cpp
index b346fdf8e..d95574bb5 100644
--- a/src/input_common/gcadapter/gc_poller.cpp
+++ b/src/input_common/gcadapter/gc_poller.cpp
@@ -15,35 +15,35 @@ namespace InputCommon {
class GCButton final : public Input::ButtonDevice {
public:
- explicit GCButton(int port_, int button_, GCAdapter::Adapter* adapter)
+ explicit GCButton(u32 port_, s32 button_, const GCAdapter::Adapter* adapter)
: port(port_), button(button_), gcadapter(adapter) {}
~GCButton() override;
bool GetStatus() const override {
if (gcadapter->DeviceConnected(port)) {
- return gcadapter->GetPadState()[port].buttons.at(button);
+ return (gcadapter->GetPadState(port).buttons & button) != 0;
}
return false;
}
private:
- const int port;
- const int button;
- GCAdapter::Adapter* gcadapter;
+ const u32 port;
+ const s32 button;
+ const GCAdapter::Adapter* gcadapter;
};
class GCAxisButton final : public Input::ButtonDevice {
public:
- explicit GCAxisButton(int port_, int axis_, float threshold_, bool trigger_if_greater_,
- GCAdapter::Adapter* adapter)
+ explicit GCAxisButton(u32 port_, u32 axis_, float threshold_, bool trigger_if_greater_,
+ const GCAdapter::Adapter* adapter)
: port(port_), axis(axis_), threshold(threshold_), trigger_if_greater(trigger_if_greater_),
- gcadapter(adapter), origin_value(adapter->GetOriginValue(port_, axis_)) {}
+ gcadapter(adapter) {}
bool GetStatus() const override {
if (gcadapter->DeviceConnected(port)) {
- const float current_axis_value = gcadapter->GetPadState()[port].axes.at(axis);
- const float axis_value = (current_axis_value - origin_value) / 128.0f;
+ const float current_axis_value = gcadapter->GetPadState(port).axis_values.at(axis);
+ const float axis_value = current_axis_value / 128.0f;
if (trigger_if_greater) {
// TODO: Might be worthwile to set a slider for the trigger threshold. It is
// currently always set to 0.5 in configure_input_player.cpp ZL/ZR HandleClick
@@ -55,12 +55,11 @@ public:
}
private:
- const int port;
- const int axis;
+ const u32 port;
+ const u32 axis;
float threshold;
bool trigger_if_greater;
- GCAdapter::Adapter* gcadapter;
- const float origin_value;
+ const GCAdapter::Adapter* gcadapter;
};
GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
@@ -69,10 +68,10 @@ GCButtonFactory::GCButtonFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
GCButton::~GCButton() = default;
std::unique_ptr<Input::ButtonDevice> GCButtonFactory::Create(const Common::ParamPackage& params) {
- const int button_id = params.Get("button", 0);
- const int port = params.Get("port", 0);
+ const auto button_id = params.Get("button", 0);
+ const auto port = static_cast<u32>(params.Get("port", 0));
- constexpr int PAD_STICK_ID = static_cast<u16>(GCAdapter::PadButton::PAD_STICK);
+ constexpr s32 PAD_STICK_ID = static_cast<s32>(GCAdapter::PadButton::Stick);
// button is not an axis/stick button
if (button_id != PAD_STICK_ID) {
@@ -105,32 +104,25 @@ Common::ParamPackage GCButtonFactory::GetNextInput() const {
Common::ParamPackage params;
GCAdapter::GCPadStatus pad;
auto& queue = adapter->GetPadQueue();
- for (std::size_t port = 0; port < queue.size(); ++port) {
- while (queue[port].Pop(pad)) {
- // This while loop will break on the earliest detected button
- params.Set("engine", "gcpad");
- params.Set("port", static_cast<int>(port));
- for (const auto& button : GCAdapter::PadButtonArray) {
- const u16 button_value = static_cast<u16>(button);
- if (pad.button & button_value) {
- params.Set("button", button_value);
- break;
- }
- }
+ while (queue.Pop(pad)) {
+ // This while loop will break on the earliest detected button
+ params.Set("engine", "gcpad");
+ params.Set("port", static_cast<s32>(pad.port));
+ if (pad.button != GCAdapter::PadButton::Undefined) {
+ params.Set("button", static_cast<u16>(pad.button));
+ }
- // For Axis button implementation
- if (pad.axis != GCAdapter::PadAxes::Undefined) {
- params.Set("axis", static_cast<u8>(pad.axis));
- params.Set("button", static_cast<u16>(GCAdapter::PadButton::PAD_STICK));
- if (pad.axis_value > 128) {
- params.Set("direction", "+");
- params.Set("threshold", "0.25");
- } else {
- params.Set("direction", "-");
- params.Set("threshold", "-0.25");
- }
- break;
+ // For Axis button implementation
+ if (pad.axis != GCAdapter::PadAxes::Undefined) {
+ params.Set("axis", static_cast<u8>(pad.axis));
+ params.Set("button", static_cast<u16>(GCAdapter::PadButton::Stick));
+ params.Set("threshold", "0.25");
+ if (pad.axis_value > 0) {
+ params.Set("direction", "+");
+ } else {
+ params.Set("direction", "-");
}
+ break;
}
}
return params;
@@ -148,24 +140,24 @@ void GCButtonFactory::EndConfiguration() {
class GCAnalog final : public Input::AnalogDevice {
public:
- GCAnalog(int port_, int axis_x_, int axis_y_, float deadzone_, GCAdapter::Adapter* adapter,
- float range_)
+ explicit GCAnalog(u32 port_, u32 axis_x_, u32 axis_y_, float deadzone_,
+ const GCAdapter::Adapter* adapter, float range_)
: port(port_), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_), gcadapter(adapter),
- origin_value_x(adapter->GetOriginValue(port_, axis_x_)),
- origin_value_y(adapter->GetOriginValue(port_, axis_y_)), range(range_) {}
+ range(range_) {}
- float GetAxis(int axis) const {
+ float GetAxis(u32 axis) const {
if (gcadapter->DeviceConnected(port)) {
std::lock_guard lock{mutex};
- const auto origin_value = axis % 2 == 0 ? origin_value_x : origin_value_y;
- return (gcadapter->GetPadState()[port].axes.at(axis) - origin_value) / (100.0f * range);
+ const auto axis_value =
+ static_cast<float>(gcadapter->GetPadState(port).axis_values.at(axis));
+ return (axis_value) / (100.0f * range);
}
return 0.0f;
}
- std::pair<float, float> GetAnalog(int axis_x, int axis_y) const {
- float x = GetAxis(axis_x);
- float y = GetAxis(axis_y);
+ std::pair<float, float> GetAnalog(u32 analog_axis_x, u32 analog_axis_y) const {
+ float x = GetAxis(analog_axis_x);
+ float y = GetAxis(analog_axis_y);
// Make sure the coordinates are in the unit circle,
// otherwise normalize it.
@@ -191,7 +183,7 @@ public:
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus();
- const float directional_deadzone = 0.4f;
+ const float directional_deadzone = 0.5f;
switch (direction) {
case Input::AnalogDirection::RIGHT:
return x > directional_deadzone;
@@ -206,13 +198,11 @@ public:
}
private:
- const int port;
- const int axis_x;
- const int axis_y;
+ const u32 port;
+ const u32 axis_x;
+ const u32 axis_y;
const float deadzone;
- GCAdapter::Adapter* gcadapter;
- const float origin_value_x;
- const float origin_value_y;
+ const GCAdapter::Adapter* gcadapter;
const float range;
mutable std::mutex mutex;
};
@@ -229,11 +219,11 @@ GCAnalogFactory::GCAnalogFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
* - "axis_y": the index of the axis to be bind as y-axis
*/
std::unique_ptr<Input::AnalogDevice> GCAnalogFactory::Create(const Common::ParamPackage& params) {
- const int port = params.Get("port", 0);
- const int axis_x = params.Get("axis_x", 0);
- const int axis_y = params.Get("axis_y", 1);
- const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
- const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
+ const auto port = static_cast<u32>(params.Get("port", 0));
+ const auto axis_x = static_cast<u32>(params.Get("axis_x", 0));
+ const auto axis_y = static_cast<u32>(params.Get("axis_y", 1));
+ const auto deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
+ const auto range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
return std::make_unique<GCAnalog>(port, axis_x, axis_y, deadzone, adapter.get(), range);
}
@@ -250,26 +240,44 @@ void GCAnalogFactory::EndConfiguration() {
Common::ParamPackage GCAnalogFactory::GetNextInput() {
GCAdapter::GCPadStatus pad;
+ Common::ParamPackage params;
auto& queue = adapter->GetPadQueue();
- for (std::size_t port = 0; port < queue.size(); ++port) {
- while (queue[port].Pop(pad)) {
- if (pad.axis == GCAdapter::PadAxes::Undefined ||
- std::abs((pad.axis_value - 128.0f) / 128.0f) < 0.1) {
- continue;
- }
- // An analog device needs two axes, so we need to store the axis for later and wait for
- // a second input event. The axes also must be from the same joystick.
- const u8 axis = static_cast<u8>(pad.axis);
- if (analog_x_axis == -1) {
- analog_x_axis = axis;
- controller_number = static_cast<int>(port);
- } else if (analog_y_axis == -1 && analog_x_axis != axis &&
- controller_number == static_cast<int>(port)) {
- analog_y_axis = axis;
- }
+ while (queue.Pop(pad)) {
+ if (pad.button != GCAdapter::PadButton::Undefined) {
+ params.Set("engine", "gcpad");
+ params.Set("port", static_cast<s32>(pad.port));
+ params.Set("button", static_cast<u16>(pad.button));
+ return params;
+ }
+ if (pad.axis == GCAdapter::PadAxes::Undefined ||
+ std::abs(static_cast<float>(pad.axis_value) / 128.0f) < 0.1f) {
+ continue;
+ }
+ // An analog device needs two axes, so we need to store the axis for later and wait for
+ // a second input event. The axes also must be from the same joystick.
+ const u8 axis = static_cast<u8>(pad.axis);
+ if (axis == 0 || axis == 1) {
+ analog_x_axis = 0;
+ analog_y_axis = 1;
+ controller_number = static_cast<s32>(pad.port);
+ break;
+ }
+ if (axis == 2 || axis == 3) {
+ analog_x_axis = 2;
+ analog_y_axis = 3;
+ controller_number = static_cast<s32>(pad.port);
+ break;
+ }
+
+ if (analog_x_axis == -1) {
+ analog_x_axis = axis;
+ controller_number = static_cast<s32>(pad.port);
+ } else if (analog_y_axis == -1 && analog_x_axis != axis &&
+ controller_number == static_cast<s32>(pad.port)) {
+ analog_y_axis = axis;
+ break;
}
}
- Common::ParamPackage params;
if (analog_x_axis != -1 && analog_y_axis != -1) {
params.Set("engine", "gcpad");
params.Set("port", controller_number);
@@ -283,4 +291,42 @@ Common::ParamPackage GCAnalogFactory::GetNextInput() {
return params;
}
+class GCVibration final : public Input::VibrationDevice {
+public:
+ explicit GCVibration(u32 port_, GCAdapter::Adapter* adapter)
+ : port(port_), gcadapter(adapter) {}
+
+ u8 GetStatus() const override {
+ return gcadapter->RumblePlay(port, 0);
+ }
+
+ bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
+ const auto mean_amplitude = (amp_low + amp_high) * 0.5f;
+ const auto processed_amplitude =
+ static_cast<u8>((mean_amplitude + std::pow(mean_amplitude, 0.3f)) * 0.5f * 0x8);
+
+ return gcadapter->RumblePlay(port, processed_amplitude);
+ }
+
+private:
+ const u32 port;
+ GCAdapter::Adapter* gcadapter;
+};
+
+/// An vibration device factory that creates vibration devices from GC Adapter
+GCVibrationFactory::GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_)
+ : adapter(std::move(adapter_)) {}
+
+/**
+ * Creates a vibration device from a joystick
+ * @param params contains parameters for creating the device:
+ * - "port": the nth gcpad on the adapter
+ */
+std::unique_ptr<Input::VibrationDevice> GCVibrationFactory::Create(
+ const Common::ParamPackage& params) {
+ const auto port = static_cast<u32>(params.Get("port", 0));
+
+ return std::make_unique<GCVibration>(port, adapter.get());
+}
+
} // 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:
bool polling = false;
};
+/// A vibration device factory creates vibration devices from GC Adapter
+class GCVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
+public:
+ explicit GCVibrationFactory(std::shared_ptr<GCAdapter::Adapter> adapter_);
+
+ std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override;
+
+private:
+ std::shared_ptr<GCAdapter::Adapter> adapter;
+};
+
} // namespace InputCommon
diff --git a/src/input_common/keyboard.cpp b/src/input_common/keyboard.cpp
index afb8e6612..24a6f7a33 100644
--- a/src/input_common/keyboard.cpp
+++ b/src/input_common/keyboard.cpp
@@ -49,8 +49,9 @@ public:
void ChangeKeyStatus(int key_code, bool pressed) {
std::lock_guard guard{mutex};
for (const KeyButtonPair& pair : list) {
- if (pair.key_code == key_code)
+ if (pair.key_code == key_code) {
pair.key_button->status.store(pressed);
+ }
}
}
@@ -73,7 +74,7 @@ KeyButton::~KeyButton() {
}
std::unique_ptr<Input::ButtonDevice> Keyboard::Create(const Common::ParamPackage& params) {
- int key_code = params.Get("code", 0);
+ const int key_code = params.Get("code", 0);
std::unique_ptr<KeyButton> button = std::make_unique<KeyButton>(key_button_list);
key_button_list->AddKeyButton(key_code, button.get());
return button;
diff --git a/src/input_common/main.cpp b/src/input_common/main.cpp
index b9d5d0ec3..e59ad4ff5 100644
--- a/src/input_common/main.cpp
+++ b/src/input_common/main.cpp
@@ -11,6 +11,9 @@
#include "input_common/keyboard.h"
#include "input_common/main.h"
#include "input_common/motion_emu.h"
+#include "input_common/motion_from_button.h"
+#include "input_common/touch_from_button.h"
+#include "input_common/udp/client.h"
#include "input_common/udp/udp.h"
#ifdef HAVE_SDL2
#include "input_common/sdl/sdl.h"
@@ -18,67 +21,228 @@
namespace InputCommon {
-static std::shared_ptr<Keyboard> keyboard;
-static std::shared_ptr<MotionEmu> motion_emu;
+struct InputSubsystem::Impl {
+ void Initialize() {
+ gcadapter = std::make_shared<GCAdapter::Adapter>();
+ gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
+ Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
+ gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
+ Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
+ gcvibration = std::make_shared<GCVibrationFactory>(gcadapter);
+ Input::RegisterFactory<Input::VibrationDevice>("gcpad", gcvibration);
+
+ keyboard = std::make_shared<Keyboard>();
+ Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
+ Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
+ std::make_shared<AnalogFromButton>());
+ Input::RegisterFactory<Input::MotionDevice>("keyboard",
+ std::make_shared<MotionFromButton>());
+ motion_emu = std::make_shared<MotionEmu>();
+ Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
+ Input::RegisterFactory<Input::TouchDevice>("touch_from_button",
+ std::make_shared<TouchFromButtonFactory>());
+
#ifdef HAVE_SDL2
-static std::unique_ptr<SDL::State> sdl;
+ sdl = SDL::Init();
#endif
-static std::unique_ptr<CemuhookUDP::State> udp;
-static std::shared_ptr<GCButtonFactory> gcbuttons;
-static std::shared_ptr<GCAnalogFactory> gcanalog;
-
-void Init() {
- auto gcadapter = std::make_shared<GCAdapter::Adapter>();
- gcbuttons = std::make_shared<GCButtonFactory>(gcadapter);
- Input::RegisterFactory<Input::ButtonDevice>("gcpad", gcbuttons);
- gcanalog = std::make_shared<GCAnalogFactory>(gcadapter);
- Input::RegisterFactory<Input::AnalogDevice>("gcpad", gcanalog);
-
- keyboard = std::make_shared<Keyboard>();
- Input::RegisterFactory<Input::ButtonDevice>("keyboard", keyboard);
- Input::RegisterFactory<Input::AnalogDevice>("analog_from_button",
- std::make_shared<AnalogFromButton>());
- motion_emu = std::make_shared<MotionEmu>();
- Input::RegisterFactory<Input::MotionDevice>("motion_emu", motion_emu);
+ udp = std::make_shared<InputCommon::CemuhookUDP::Client>();
+ udpmotion = std::make_shared<UDPMotionFactory>(udp);
+ Input::RegisterFactory<Input::MotionDevice>("cemuhookudp", udpmotion);
+ udptouch = std::make_shared<UDPTouchFactory>(udp);
+ Input::RegisterFactory<Input::TouchDevice>("cemuhookudp", udptouch);
+ }
+
+ void Shutdown() {
+ Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
+ Input::UnregisterFactory<Input::MotionDevice>("keyboard");
+ keyboard.reset();
+ Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
+ Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
+ motion_emu.reset();
+ Input::UnregisterFactory<Input::TouchDevice>("touch_from_button");
#ifdef HAVE_SDL2
- sdl = SDL::Init();
+ sdl.reset();
#endif
+ Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
+ Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
+ Input::UnregisterFactory<Input::VibrationDevice>("gcpad");
- udp = CemuhookUDP::Init();
-}
+ gcbuttons.reset();
+ gcanalog.reset();
+ gcvibration.reset();
-void Shutdown() {
- Input::UnregisterFactory<Input::ButtonDevice>("keyboard");
- keyboard.reset();
- Input::UnregisterFactory<Input::AnalogDevice>("analog_from_button");
- Input::UnregisterFactory<Input::MotionDevice>("motion_emu");
- motion_emu.reset();
+ Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
+ Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
+
+ udpmotion.reset();
+ udptouch.reset();
+ }
+
+ [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const {
+ std::vector<Common::ParamPackage> devices = {
+ Common::ParamPackage{{"display", "Any"}, {"class", "any"}},
+ Common::ParamPackage{{"display", "Keyboard/Mouse"}, {"class", "keyboard"}},
+ };
#ifdef HAVE_SDL2
- sdl.reset();
+ auto sdl_devices = sdl->GetInputDevices();
+ devices.insert(devices.end(), sdl_devices.begin(), sdl_devices.end());
#endif
- udp.reset();
- Input::UnregisterFactory<Input::ButtonDevice>("gcpad");
- Input::UnregisterFactory<Input::AnalogDevice>("gcpad");
+ auto udp_devices = udp->GetInputDevices();
+ devices.insert(devices.end(), udp_devices.begin(), udp_devices.end());
+ auto gcpad_devices = gcadapter->GetInputDevices();
+ devices.insert(devices.end(), gcpad_devices.begin(), gcpad_devices.end());
+ return devices;
+ }
+
+ [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(
+ const Common::ParamPackage& params) const {
+ if (!params.Has("class") || params.Get("class", "") == "any") {
+ return {};
+ }
+ if (params.Get("class", "") == "gcpad") {
+ return gcadapter->GetAnalogMappingForDevice(params);
+ }
+#ifdef HAVE_SDL2
+ if (params.Get("class", "") == "sdl") {
+ return sdl->GetAnalogMappingForDevice(params);
+ }
+#endif
+ return {};
+ }
+
+ [[nodiscard]] ButtonMapping GetButtonMappingForDevice(
+ const Common::ParamPackage& params) const {
+ if (!params.Has("class") || params.Get("class", "") == "any") {
+ return {};
+ }
+ if (params.Get("class", "") == "gcpad") {
+ return gcadapter->GetButtonMappingForDevice(params);
+ }
+#ifdef HAVE_SDL2
+ if (params.Get("class", "") == "sdl") {
+ return sdl->GetButtonMappingForDevice(params);
+ }
+#endif
+ return {};
+ }
+
+ [[nodiscard]] MotionMapping GetMotionMappingForDevice(
+ const Common::ParamPackage& params) const {
+ if (!params.Has("class") || params.Get("class", "") == "any") {
+ return {};
+ }
+ if (params.Get("class", "") == "cemuhookudp") {
+ // TODO return the correct motion device
+ return {};
+ }
+ return {};
+ }
+
+ std::shared_ptr<Keyboard> keyboard;
+ std::shared_ptr<MotionEmu> motion_emu;
+#ifdef HAVE_SDL2
+ std::unique_ptr<SDL::State> sdl;
+#endif
+ std::shared_ptr<GCButtonFactory> gcbuttons;
+ std::shared_ptr<GCAnalogFactory> gcanalog;
+ std::shared_ptr<GCVibrationFactory> gcvibration;
+ std::shared_ptr<UDPMotionFactory> udpmotion;
+ std::shared_ptr<UDPTouchFactory> udptouch;
+ std::shared_ptr<CemuhookUDP::Client> udp;
+ std::shared_ptr<GCAdapter::Adapter> gcadapter;
+};
+
+InputSubsystem::InputSubsystem() : impl{std::make_unique<Impl>()} {}
+
+InputSubsystem::~InputSubsystem() = default;
+
+void InputSubsystem::Initialize() {
+ impl->Initialize();
+}
+
+void InputSubsystem::Shutdown() {
+ impl->Shutdown();
+}
+
+Keyboard* InputSubsystem::GetKeyboard() {
+ return impl->keyboard.get();
+}
+
+const Keyboard* InputSubsystem::GetKeyboard() const {
+ return impl->keyboard.get();
+}
+
+MotionEmu* InputSubsystem::GetMotionEmu() {
+ return impl->motion_emu.get();
+}
+
+const MotionEmu* InputSubsystem::GetMotionEmu() const {
+ return impl->motion_emu.get();
+}
+
+std::vector<Common::ParamPackage> InputSubsystem::GetInputDevices() const {
+ return impl->GetInputDevices();
+}
+
+AnalogMapping InputSubsystem::GetAnalogMappingForDevice(const Common::ParamPackage& device) const {
+ return impl->GetAnalogMappingForDevice(device);
+}
+
+ButtonMapping InputSubsystem::GetButtonMappingForDevice(const Common::ParamPackage& device) const {
+ return impl->GetButtonMappingForDevice(device);
+}
+
+MotionMapping InputSubsystem::GetMotionMappingForDevice(const Common::ParamPackage& device) const {
+ return impl->GetMotionMappingForDevice(device);
+}
+
+GCAnalogFactory* InputSubsystem::GetGCAnalogs() {
+ return impl->gcanalog.get();
+}
+
+const GCAnalogFactory* InputSubsystem::GetGCAnalogs() const {
+ return impl->gcanalog.get();
+}
+
+GCButtonFactory* InputSubsystem::GetGCButtons() {
+ return impl->gcbuttons.get();
+}
- gcbuttons.reset();
- gcanalog.reset();
+const GCButtonFactory* InputSubsystem::GetGCButtons() const {
+ return impl->gcbuttons.get();
}
-Keyboard* GetKeyboard() {
- return keyboard.get();
+UDPMotionFactory* InputSubsystem::GetUDPMotions() {
+ return impl->udpmotion.get();
}
-MotionEmu* GetMotionEmu() {
- return motion_emu.get();
+const UDPMotionFactory* InputSubsystem::GetUDPMotions() const {
+ return impl->udpmotion.get();
}
-GCButtonFactory* GetGCButtons() {
- return gcbuttons.get();
+UDPTouchFactory* InputSubsystem::GetUDPTouch() {
+ return impl->udptouch.get();
}
-GCAnalogFactory* GetGCAnalogs() {
- return gcanalog.get();
+const UDPTouchFactory* InputSubsystem::GetUDPTouch() const {
+ return impl->udptouch.get();
+}
+
+void InputSubsystem::ReloadInputDevices() {
+ if (!impl->udp) {
+ return;
+ }
+ impl->udp->ReloadUDPClient();
+}
+
+std::vector<std::unique_ptr<Polling::DevicePoller>> InputSubsystem::GetPollers(
+ Polling::DeviceType type) const {
+#ifdef HAVE_SDL2
+ return impl->sdl->GetPollers(type);
+#else
+ return {};
+#endif
}
std::string GenerateKeyboardParam(int key_code) {
@@ -102,18 +266,4 @@ std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left,
};
return circle_pad_param.Serialize();
}
-
-namespace Polling {
-
-std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type) {
- std::vector<std::unique_ptr<DevicePoller>> pollers;
-
-#ifdef HAVE_SDL2
- pollers = sdl->GetPollers(type);
-#endif
-
- return pollers;
-}
-
-} // namespace Polling
} // namespace InputCommon
diff --git a/src/input_common/main.h b/src/input_common/main.h
index 0e32856f6..dded3f1ef 100644
--- a/src/input_common/main.h
+++ b/src/input_common/main.h
@@ -6,45 +6,29 @@
#include <memory>
#include <string>
+#include <unordered_map>
#include <vector>
-#include "input_common/gcadapter/gc_poller.h"
namespace Common {
class ParamPackage;
}
-namespace InputCommon {
-
-/// Initializes and registers all built-in input device factories.
-void Init();
-
-/// Deregisters all built-in input device factories and shuts them down.
-void Shutdown();
-
-class Keyboard;
-
-/// Gets the keyboard button device factory.
-Keyboard* GetKeyboard();
-
-class MotionEmu;
-
-/// Gets the motion emulation factory.
-MotionEmu* GetMotionEmu();
-
-GCButtonFactory* GetGCButtons();
-
-GCAnalogFactory* GetGCAnalogs();
+namespace Settings::NativeAnalog {
+enum Values : int;
+}
-/// Generates a serialized param package for creating a keyboard button device
-std::string GenerateKeyboardParam(int key_code);
+namespace Settings::NativeButton {
+enum Values : int;
+}
-/// Generates a serialized param package for creating an analog device taking input from keyboard
-std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
- int key_modifier, float modifier_scale);
+namespace Settings::NativeMotion {
+enum Values : int;
+}
+namespace InputCommon {
namespace Polling {
-enum class DeviceType { Button, Analog };
+enum class DeviceType { Button, AnalogPreferred, Motion };
/**
* A class that can be used to get inputs from an input device like controllers without having to
@@ -54,7 +38,9 @@ class DevicePoller {
public:
virtual ~DevicePoller() = default;
/// Setup and start polling for inputs, should be called before GetNextInput
- virtual void Start() = 0;
+ /// If a device_id is provided, events should be filtered to only include events from this
+ /// device id
+ virtual void Start(const std::string& device_id = "") = 0;
/// Stop polling
virtual void Stop() = 0;
/**
@@ -64,8 +50,110 @@ public:
*/
virtual Common::ParamPackage GetNextInput() = 0;
};
-
-// Get all DevicePoller from all backends for a specific device type
-std::vector<std::unique_ptr<DevicePoller>> GetPollers(DeviceType type);
} // namespace Polling
+
+class GCAnalogFactory;
+class GCButtonFactory;
+class UDPMotionFactory;
+class UDPTouchFactory;
+class Keyboard;
+class MotionEmu;
+
+/**
+ * Given a ParamPackage for a Device returned from `GetInputDevices`, attempt to get the default
+ * mapping for the device. This is currently only implemented for the SDL backend devices.
+ */
+using AnalogMapping = std::unordered_map<Settings::NativeAnalog::Values, Common::ParamPackage>;
+using ButtonMapping = std::unordered_map<Settings::NativeButton::Values, Common::ParamPackage>;
+using MotionMapping = std::unordered_map<Settings::NativeMotion::Values, Common::ParamPackage>;
+
+class InputSubsystem {
+public:
+ explicit InputSubsystem();
+ ~InputSubsystem();
+
+ InputSubsystem(const InputSubsystem&) = delete;
+ InputSubsystem& operator=(const InputSubsystem&) = delete;
+
+ InputSubsystem(InputSubsystem&&) = delete;
+ InputSubsystem& operator=(InputSubsystem&&) = delete;
+
+ /// Initializes and registers all built-in input device factories.
+ void Initialize();
+
+ /// Unregisters all built-in input device factories and shuts them down.
+ void Shutdown();
+
+ /// Retrieves the underlying keyboard device.
+ [[nodiscard]] Keyboard* GetKeyboard();
+
+ /// Retrieves the underlying keyboard device.
+ [[nodiscard]] const Keyboard* GetKeyboard() const;
+
+ /// Retrieves the underlying motion emulation factory.
+ [[nodiscard]] MotionEmu* GetMotionEmu();
+
+ /// Retrieves the underlying motion emulation factory.
+ [[nodiscard]] const MotionEmu* GetMotionEmu() const;
+
+ /**
+ * Returns all available input devices that this Factory can create a new device with.
+ * Each returned ParamPackage should have a `display` field used for display, a class field for
+ * backends to determine if this backend is meant to service the request and any other
+ * information needed to identify this in the backend later.
+ */
+ [[nodiscard]] std::vector<Common::ParamPackage> GetInputDevices() const;
+
+ /// Retrieves the analog mappings for the given device.
+ [[nodiscard]] AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& device) const;
+
+ /// Retrieves the button mappings for the given device.
+ [[nodiscard]] ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& device) const;
+
+ /// Retrieves the motion mappings for the given device.
+ [[nodiscard]] MotionMapping GetMotionMappingForDevice(const Common::ParamPackage& device) const;
+
+ /// Retrieves the underlying GameCube analog handler.
+ [[nodiscard]] GCAnalogFactory* GetGCAnalogs();
+
+ /// Retrieves the underlying GameCube analog handler.
+ [[nodiscard]] const GCAnalogFactory* GetGCAnalogs() const;
+
+ /// Retrieves the underlying GameCube button handler.
+ [[nodiscard]] GCButtonFactory* GetGCButtons();
+
+ /// Retrieves the underlying GameCube button handler.
+ [[nodiscard]] const GCButtonFactory* GetGCButtons() const;
+
+ /// Retrieves the underlying udp motion handler.
+ [[nodiscard]] UDPMotionFactory* GetUDPMotions();
+
+ /// Retrieves the underlying udp motion handler.
+ [[nodiscard]] const UDPMotionFactory* GetUDPMotions() const;
+
+ /// Retrieves the underlying udp touch handler.
+ [[nodiscard]] UDPTouchFactory* GetUDPTouch();
+
+ /// Retrieves the underlying udp touch handler.
+ [[nodiscard]] const UDPTouchFactory* GetUDPTouch() const;
+
+ /// Reloads the input devices
+ void ReloadInputDevices();
+
+ /// Get all DevicePoller from all backends for a specific device type
+ [[nodiscard]] std::vector<std::unique_ptr<Polling::DevicePoller>> GetPollers(
+ Polling::DeviceType type) const;
+
+private:
+ struct Impl;
+ std::unique_ptr<Impl> impl;
+};
+
+/// Generates a serialized param package for creating a keyboard button device
+std::string GenerateKeyboardParam(int key_code);
+
+/// Generates a serialized param package for creating an analog device taking input from keyboard
+std::string GenerateAnalogParamFromKeys(int key_up, int key_down, int key_left, int key_right,
+ int key_modifier, float modifier_scale);
+
} // namespace InputCommon
diff --git a/src/input_common/motion_emu.cpp b/src/input_common/motion_emu.cpp
index d4cdf76a3..d4da5596b 100644
--- a/src/input_common/motion_emu.cpp
+++ b/src/input_common/motion_emu.cpp
@@ -18,11 +18,11 @@ namespace InputCommon {
// Implementation class of the motion emulation device
class MotionEmuDevice {
public:
- MotionEmuDevice(int update_millisecond, float sensitivity)
- : update_millisecond(update_millisecond),
+ explicit MotionEmuDevice(int update_millisecond_, float sensitivity_)
+ : update_millisecond(update_millisecond_),
update_duration(std::chrono::duration_cast<std::chrono::steady_clock::duration>(
std::chrono::milliseconds(update_millisecond))),
- sensitivity(sensitivity), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
+ sensitivity(sensitivity_), motion_emu_thread(&MotionEmuDevice::MotionEmuThread, this) {}
~MotionEmuDevice() {
if (motion_emu_thread.joinable()) {
@@ -37,16 +37,18 @@ public:
}
void Tilt(int x, int y) {
- auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
- if (is_tilting) {
- std::lock_guard guard{tilt_mutex};
- if (mouse_move.x == 0 && mouse_move.y == 0) {
- tilt_angle = 0;
- } else {
- tilt_direction = mouse_move.Cast<float>();
- tilt_angle =
- std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f);
- }
+ if (!is_tilting) {
+ return;
+ }
+
+ std::lock_guard guard{tilt_mutex};
+ const auto mouse_move = Common::MakeVec(x, y) - mouse_origin;
+ if (mouse_move.x == 0 && mouse_move.y == 0) {
+ tilt_angle = 0;
+ } else {
+ tilt_direction = mouse_move.Cast<float>();
+ tilt_angle =
+ std::clamp(tilt_direction.Normalize() * sensitivity, 0.0f, Common::PI * 0.5f);
}
}
@@ -56,7 +58,7 @@ public:
is_tilting = false;
}
- std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() {
+ Input::MotionStatus GetStatus() {
std::lock_guard guard{status_mutex};
return status;
}
@@ -76,7 +78,7 @@ private:
Common::Event shutdown_event;
- std::tuple<Common::Vec3<float>, Common::Vec3<float>> status;
+ Input::MotionStatus status;
std::mutex status_mutex;
// Note: always keep the thread declaration at the end so that other objects are initialized
@@ -86,11 +88,10 @@ private:
void MotionEmuThread() {
auto update_time = std::chrono::steady_clock::now();
Common::Quaternion<float> q = Common::MakeQuaternion(Common::Vec3<float>(), 0);
- Common::Quaternion<float> old_q;
while (!shutdown_event.WaitUntil(update_time)) {
update_time += update_duration;
- old_q = q;
+ const Common::Quaternion<float> old_q = q;
{
std::lock_guard guard{tilt_mutex};
@@ -100,23 +101,32 @@ private:
Common::MakeVec(-tilt_direction.y, 0.0f, tilt_direction.x), tilt_angle);
}
- auto inv_q = q.Inverse();
+ const auto inv_q = q.Inverse();
// Set the gravity vector in world space
auto gravity = Common::MakeVec(0.0f, -1.0f, 0.0f);
// Find the angular rate vector in world space
auto angular_rate = ((q - old_q) * inv_q).xyz * 2;
- angular_rate *= 1000 / update_millisecond / Common::PI * 180;
+ angular_rate *= static_cast<float>(1000 / update_millisecond) / Common::PI * 180.0f;
// Transform the two vectors from world space to 3DS space
gravity = QuaternionRotate(inv_q, gravity);
angular_rate = QuaternionRotate(inv_q, angular_rate);
+ // TODO: Calculate the correct rotation vector and orientation matrix
+ const auto matrix4x4 = q.ToMatrix();
+ const auto rotation = Common::MakeVec(0.0f, 0.0f, 0.0f);
+ const std::array orientation{
+ Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
+ Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
+ Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10]),
+ };
+
// Update the sensor state
{
std::lock_guard guard{status_mutex};
- status = std::make_tuple(gravity, angular_rate);
+ status = std::make_tuple(gravity, angular_rate, rotation, orientation);
}
}
}
@@ -127,11 +137,11 @@ private:
// can forward all the inputs to the implementation only when it is valid.
class MotionEmuDeviceWrapper : public Input::MotionDevice {
public:
- MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
+ explicit MotionEmuDeviceWrapper(int update_millisecond, float sensitivity) {
device = std::make_shared<MotionEmuDevice>(update_millisecond, sensitivity);
}
- std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
+ Input::MotionStatus GetStatus() const override {
return device->GetStatus();
}
@@ -139,8 +149,8 @@ public:
};
std::unique_ptr<Input::MotionDevice> MotionEmu::Create(const Common::ParamPackage& params) {
- int update_period = params.Get("update_period", 100);
- float sensitivity = params.Get("sensitivity", 0.01f);
+ const int update_period = params.Get("update_period", 100);
+ const float sensitivity = params.Get("sensitivity", 0.01f);
auto device_wrapper = std::make_unique<MotionEmuDeviceWrapper>(update_period, sensitivity);
// Previously created device is disconnected here. Having two motion devices for 3DS is not
// expected.
diff --git a/src/input_common/motion_from_button.cpp b/src/input_common/motion_from_button.cpp
new file mode 100644
index 000000000..29045a673
--- /dev/null
+++ b/src/input_common/motion_from_button.cpp
@@ -0,0 +1,34 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "input_common/motion_from_button.h"
+#include "input_common/motion_input.h"
+
+namespace InputCommon {
+
+class MotionKey final : public Input::MotionDevice {
+public:
+ using Button = std::unique_ptr<Input::ButtonDevice>;
+
+ explicit MotionKey(Button key_) : key(std::move(key_)) {}
+
+ Input::MotionStatus GetStatus() const override {
+
+ if (key->GetStatus()) {
+ return motion.GetRandomMotion(2, 6);
+ }
+ return motion.GetRandomMotion(0, 0);
+ }
+
+private:
+ Button key;
+ InputCommon::MotionInput motion{0.0f, 0.0f, 0.0f};
+};
+
+std::unique_ptr<Input::MotionDevice> MotionFromButton::Create(const Common::ParamPackage& params) {
+ auto key = Input::CreateDevice<Input::ButtonDevice>(params.Serialize());
+ return std::make_unique<MotionKey>(std::move(key));
+}
+
+} // namespace InputCommon
diff --git a/src/input_common/motion_from_button.h b/src/input_common/motion_from_button.h
new file mode 100644
index 000000000..a959046fb
--- /dev/null
+++ b/src/input_common/motion_from_button.h
@@ -0,0 +1,25 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "core/frontend/input.h"
+
+namespace InputCommon {
+
+/**
+ * An motion device factory that takes a keyboard button and uses it as a random
+ * motion device.
+ */
+class MotionFromButton final : public Input::Factory<Input::MotionDevice> {
+public:
+ /**
+ * Creates an motion device from button devices
+ * @param params contains parameters for creating the device:
+ * - "key": a serialized ParamPackage for creating a button device
+ */
+ std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
+};
+
+} // namespace InputCommon
diff --git a/src/input_common/motion_input.cpp b/src/input_common/motion_input.cpp
new file mode 100644
index 000000000..f77ba535d
--- /dev/null
+++ b/src/input_common/motion_input.cpp
@@ -0,0 +1,301 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#include <random>
+#include "common/math_util.h"
+#include "input_common/motion_input.h"
+
+namespace InputCommon {
+
+MotionInput::MotionInput(f32 new_kp, f32 new_ki, f32 new_kd) : kp(new_kp), ki(new_ki), kd(new_kd) {}
+
+void MotionInput::SetAcceleration(const Common::Vec3f& acceleration) {
+ accel = acceleration;
+}
+
+void MotionInput::SetGyroscope(const Common::Vec3f& gyroscope) {
+ gyro = gyroscope - gyro_drift;
+
+ // Auto adjust drift to minimize drift
+ if (!IsMoving(0.1f)) {
+ gyro_drift = (gyro_drift * 0.9999f) + (gyroscope * 0.0001f);
+ }
+
+ if (gyro.Length2() < gyro_threshold) {
+ gyro = {};
+ } else {
+ only_accelerometer = false;
+ }
+}
+
+void MotionInput::SetQuaternion(const Common::Quaternion<f32>& quaternion) {
+ quat = quaternion;
+}
+
+void MotionInput::SetGyroDrift(const Common::Vec3f& drift) {
+ gyro_drift = drift;
+}
+
+void MotionInput::SetGyroThreshold(f32 threshold) {
+ gyro_threshold = threshold;
+}
+
+void MotionInput::EnableReset(bool reset) {
+ reset_enabled = reset;
+}
+
+void MotionInput::ResetRotations() {
+ rotations = {};
+}
+
+bool MotionInput::IsMoving(f32 sensitivity) const {
+ return gyro.Length() >= sensitivity || accel.Length() <= 0.9f || accel.Length() >= 1.1f;
+}
+
+bool MotionInput::IsCalibrated(f32 sensitivity) const {
+ return real_error.Length() < sensitivity;
+}
+
+void MotionInput::UpdateRotation(u64 elapsed_time) {
+ const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
+ if (sample_period > 0.1f) {
+ return;
+ }
+ rotations += gyro * sample_period;
+}
+
+void MotionInput::UpdateOrientation(u64 elapsed_time) {
+ if (!IsCalibrated(0.1f)) {
+ ResetOrientation();
+ }
+ // Short name local variable for readability
+ f32 q1 = quat.w;
+ f32 q2 = quat.xyz[0];
+ f32 q3 = quat.xyz[1];
+ f32 q4 = quat.xyz[2];
+ const auto sample_period = static_cast<f32>(elapsed_time) / 1000000.0f;
+
+ // Ignore invalid elapsed time
+ if (sample_period > 0.1f) {
+ return;
+ }
+
+ const auto normal_accel = accel.Normalized();
+ auto rad_gyro = gyro * Common::PI * 2;
+ const f32 swap = rad_gyro.x;
+ rad_gyro.x = rad_gyro.y;
+ rad_gyro.y = -swap;
+ rad_gyro.z = -rad_gyro.z;
+
+ // Clear gyro values if there is no gyro present
+ if (only_accelerometer) {
+ rad_gyro.x = 0;
+ rad_gyro.y = 0;
+ rad_gyro.z = 0;
+ }
+
+ // Ignore drift correction if acceleration is not reliable
+ if (accel.Length() >= 0.75f && accel.Length() <= 1.25f) {
+ const f32 ax = -normal_accel.x;
+ const f32 ay = normal_accel.y;
+ const f32 az = -normal_accel.z;
+
+ // Estimated direction of gravity
+ const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
+ const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
+ const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
+
+ // Error is cross product between estimated direction and measured direction of gravity
+ const Common::Vec3f new_real_error = {
+ az * vx - ax * vz,
+ ay * vz - az * vy,
+ ax * vy - ay * vx,
+ };
+
+ derivative_error = new_real_error - real_error;
+ real_error = new_real_error;
+
+ // Prevent integral windup
+ if (ki != 0.0f && !IsCalibrated(0.05f)) {
+ integral_error += real_error;
+ } else {
+ integral_error = {};
+ }
+
+ // Apply feedback terms
+ if (!only_accelerometer) {
+ rad_gyro += kp * real_error;
+ rad_gyro += ki * integral_error;
+ rad_gyro += kd * derivative_error;
+ } else {
+ // Give more weight to acelerometer values to compensate for the lack of gyro
+ rad_gyro += 35.0f * kp * real_error;
+ rad_gyro += 10.0f * ki * integral_error;
+ rad_gyro += 10.0f * kd * derivative_error;
+
+ // Emulate gyro values for games that need them
+ gyro.x = -rad_gyro.y;
+ gyro.y = rad_gyro.x;
+ gyro.z = -rad_gyro.z;
+ UpdateRotation(elapsed_time);
+ }
+ }
+
+ const f32 gx = rad_gyro.y;
+ const f32 gy = rad_gyro.x;
+ const f32 gz = rad_gyro.z;
+
+ // Integrate rate of change of quaternion
+ const f32 pa = q2;
+ const f32 pb = q3;
+ const f32 pc = q4;
+ q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
+ q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
+ q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
+ q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
+
+ quat.w = q1;
+ quat.xyz[0] = q2;
+ quat.xyz[1] = q3;
+ quat.xyz[2] = q4;
+ quat = quat.Normalized();
+}
+
+std::array<Common::Vec3f, 3> MotionInput::GetOrientation() const {
+ const Common::Quaternion<float> quad{
+ .xyz = {-quat.xyz[1], -quat.xyz[0], -quat.w},
+ .w = -quat.xyz[2],
+ };
+ const std::array<float, 16> matrix4x4 = quad.ToMatrix();
+
+ return {Common::Vec3f(matrix4x4[0], matrix4x4[1], -matrix4x4[2]),
+ Common::Vec3f(matrix4x4[4], matrix4x4[5], -matrix4x4[6]),
+ Common::Vec3f(-matrix4x4[8], -matrix4x4[9], matrix4x4[10])};
+}
+
+Common::Vec3f MotionInput::GetAcceleration() const {
+ return accel;
+}
+
+Common::Vec3f MotionInput::GetGyroscope() const {
+ return gyro;
+}
+
+Common::Quaternion<f32> MotionInput::GetQuaternion() const {
+ return quat;
+}
+
+Common::Vec3f MotionInput::GetRotations() const {
+ return rotations;
+}
+
+Input::MotionStatus MotionInput::GetMotion() const {
+ const Common::Vec3f gyroscope = GetGyroscope();
+ const Common::Vec3f accelerometer = GetAcceleration();
+ const Common::Vec3f rotation = GetRotations();
+ const std::array<Common::Vec3f, 3> orientation = GetOrientation();
+ return {accelerometer, gyroscope, rotation, orientation};
+}
+
+Input::MotionStatus MotionInput::GetRandomMotion(int accel_magnitude, int gyro_magnitude) const {
+ std::random_device device;
+ std::mt19937 gen(device());
+ std::uniform_int_distribution<s16> distribution(-1000, 1000);
+ const Common::Vec3f gyroscope{
+ static_cast<f32>(distribution(gen)) * 0.001f,
+ static_cast<f32>(distribution(gen)) * 0.001f,
+ static_cast<f32>(distribution(gen)) * 0.001f,
+ };
+ const Common::Vec3f accelerometer{
+ static_cast<f32>(distribution(gen)) * 0.001f,
+ static_cast<f32>(distribution(gen)) * 0.001f,
+ static_cast<f32>(distribution(gen)) * 0.001f,
+ };
+ constexpr Common::Vec3f rotation;
+ constexpr std::array orientation{
+ Common::Vec3f{1.0f, 0.0f, 0.0f},
+ Common::Vec3f{0.0f, 1.0f, 0.0f},
+ Common::Vec3f{0.0f, 0.0f, 1.0f},
+ };
+ return {accelerometer * accel_magnitude, gyroscope * gyro_magnitude, rotation, orientation};
+}
+
+void MotionInput::ResetOrientation() {
+ if (!reset_enabled || only_accelerometer) {
+ return;
+ }
+ if (!IsMoving(0.5f) && accel.z <= -0.9f) {
+ ++reset_counter;
+ if (reset_counter > 900) {
+ quat.w = 0;
+ quat.xyz[0] = 0;
+ quat.xyz[1] = 0;
+ quat.xyz[2] = -1;
+ SetOrientationFromAccelerometer();
+ integral_error = {};
+ reset_counter = 0;
+ }
+ } else {
+ reset_counter = 0;
+ }
+}
+
+void MotionInput::SetOrientationFromAccelerometer() {
+ int iterations = 0;
+ const f32 sample_period = 0.015f;
+
+ const auto normal_accel = accel.Normalized();
+
+ while (!IsCalibrated(0.01f) && ++iterations < 100) {
+ // Short name local variable for readability
+ f32 q1 = quat.w;
+ f32 q2 = quat.xyz[0];
+ f32 q3 = quat.xyz[1];
+ f32 q4 = quat.xyz[2];
+
+ Common::Vec3f rad_gyro;
+ const f32 ax = -normal_accel.x;
+ const f32 ay = normal_accel.y;
+ const f32 az = -normal_accel.z;
+
+ // Estimated direction of gravity
+ const f32 vx = 2.0f * (q2 * q4 - q1 * q3);
+ const f32 vy = 2.0f * (q1 * q2 + q3 * q4);
+ const f32 vz = q1 * q1 - q2 * q2 - q3 * q3 + q4 * q4;
+
+ // Error is cross product between estimated direction and measured direction of gravity
+ const Common::Vec3f new_real_error = {
+ az * vx - ax * vz,
+ ay * vz - az * vy,
+ ax * vy - ay * vx,
+ };
+
+ derivative_error = new_real_error - real_error;
+ real_error = new_real_error;
+
+ rad_gyro += 10.0f * kp * real_error;
+ rad_gyro += 5.0f * ki * integral_error;
+ rad_gyro += 10.0f * kd * derivative_error;
+
+ const f32 gx = rad_gyro.y;
+ const f32 gy = rad_gyro.x;
+ const f32 gz = rad_gyro.z;
+
+ // Integrate rate of change of quaternion
+ const f32 pa = q2;
+ const f32 pb = q3;
+ const f32 pc = q4;
+ q1 = q1 + (-q2 * gx - q3 * gy - q4 * gz) * (0.5f * sample_period);
+ q2 = pa + (q1 * gx + pb * gz - pc * gy) * (0.5f * sample_period);
+ q3 = pb + (q1 * gy - pa * gz + pc * gx) * (0.5f * sample_period);
+ q4 = pc + (q1 * gz + pa * gy - pb * gx) * (0.5f * sample_period);
+
+ quat.w = q1;
+ quat.xyz[0] = q2;
+ quat.xyz[1] = q3;
+ quat.xyz[2] = q4;
+ quat = quat.Normalized();
+ }
+}
+} // namespace InputCommon
diff --git a/src/input_common/motion_input.h b/src/input_common/motion_input.h
new file mode 100644
index 000000000..efe74cf19
--- /dev/null
+++ b/src/input_common/motion_input.h
@@ -0,0 +1,74 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included
+
+#pragma once
+
+#include "common/common_types.h"
+#include "common/quaternion.h"
+#include "common/vector_math.h"
+#include "core/frontend/input.h"
+
+namespace InputCommon {
+
+class MotionInput {
+public:
+ explicit MotionInput(f32 new_kp, f32 new_ki, f32 new_kd);
+
+ MotionInput(const MotionInput&) = default;
+ MotionInput& operator=(const MotionInput&) = default;
+
+ MotionInput(MotionInput&&) = default;
+ MotionInput& operator=(MotionInput&&) = default;
+
+ void SetAcceleration(const Common::Vec3f& acceleration);
+ void SetGyroscope(const Common::Vec3f& gyroscope);
+ void SetQuaternion(const Common::Quaternion<f32>& quaternion);
+ void SetGyroDrift(const Common::Vec3f& drift);
+ void SetGyroThreshold(f32 threshold);
+
+ void EnableReset(bool reset);
+ void ResetRotations();
+
+ void UpdateRotation(u64 elapsed_time);
+ void UpdateOrientation(u64 elapsed_time);
+
+ [[nodiscard]] std::array<Common::Vec3f, 3> GetOrientation() const;
+ [[nodiscard]] Common::Vec3f GetAcceleration() const;
+ [[nodiscard]] Common::Vec3f GetGyroscope() const;
+ [[nodiscard]] Common::Vec3f GetRotations() const;
+ [[nodiscard]] Common::Quaternion<f32> GetQuaternion() const;
+ [[nodiscard]] Input::MotionStatus GetMotion() const;
+ [[nodiscard]] Input::MotionStatus GetRandomMotion(int accel_magnitude,
+ int gyro_magnitude) const;
+
+ [[nodiscard]] bool IsMoving(f32 sensitivity) const;
+ [[nodiscard]] bool IsCalibrated(f32 sensitivity) const;
+
+private:
+ void ResetOrientation();
+ void SetOrientationFromAccelerometer();
+
+ // PID constants
+ f32 kp;
+ f32 ki;
+ f32 kd;
+
+ // PID errors
+ Common::Vec3f real_error;
+ Common::Vec3f integral_error;
+ Common::Vec3f derivative_error;
+
+ Common::Quaternion<f32> quat{{0.0f, 0.0f, -1.0f}, 0.0f};
+ Common::Vec3f rotations;
+ Common::Vec3f accel;
+ Common::Vec3f gyro;
+ Common::Vec3f gyro_drift;
+
+ f32 gyro_threshold = 0.0f;
+ u32 reset_counter = 0;
+ bool reset_enabled = true;
+ bool only_accelerometer = true;
+};
+
+} // namespace InputCommon
diff --git a/src/input_common/sdl/sdl.h b/src/input_common/sdl/sdl.h
index 5306daa70..f3554be9a 100644
--- a/src/input_common/sdl/sdl.h
+++ b/src/input_common/sdl/sdl.h
@@ -6,6 +6,7 @@
#include <memory>
#include <vector>
+#include "common/param_package.h"
#include "input_common/main.h"
namespace InputCommon::Polling {
@@ -22,14 +23,24 @@ public:
/// Unregisters SDL device factories and shut them down.
virtual ~State() = default;
- virtual Pollers GetPollers(Polling::DeviceType type) = 0;
+ virtual Pollers GetPollers(Polling::DeviceType type) {
+ return {};
+ }
+
+ virtual std::vector<Common::ParamPackage> GetInputDevices() {
+ return {};
+ }
+
+ virtual ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage&) {
+ return {};
+ }
+ virtual AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage&) {
+ return {};
+ }
};
class NullState : public State {
public:
- Pollers GetPollers(Polling::DeviceType type) override {
- return {};
- }
};
std::unique_ptr<State> Init();
diff --git a/src/input_common/sdl/sdl_impl.cpp b/src/input_common/sdl/sdl_impl.cpp
index d76c279d3..c395d96cf 100644
--- a/src/input_common/sdl/sdl_impl.cpp
+++ b/src/input_common/sdl/sdl_impl.cpp
@@ -3,10 +3,14 @@
// Refer to the license.txt file included.
#include <algorithm>
+#include <array>
#include <atomic>
+#include <chrono>
#include <cmath>
#include <functional>
#include <mutex>
+#include <optional>
+#include <sstream>
#include <string>
#include <thread>
#include <tuple>
@@ -15,15 +19,17 @@
#include <vector>
#include <SDL.h>
#include "common/logging/log.h"
-#include "common/math_util.h"
#include "common/param_package.h"
#include "common/threadsafe_queue.h"
#include "core/frontend/input.h"
+#include "input_common/motion_input.h"
#include "input_common/sdl/sdl_impl.h"
+#include "input_common/settings.h"
namespace InputCommon::SDL {
-static std::string GetGUID(SDL_Joystick* joystick) {
+namespace {
+std::string GetGUID(SDL_Joystick* joystick) {
const SDL_JoystickGUID guid = SDL_JoystickGetGUID(joystick);
char guid_str[33];
SDL_JoystickGetGUIDString(guid, guid_str, sizeof(guid_str));
@@ -31,7 +37,8 @@ static std::string GetGUID(SDL_Joystick* joystick) {
}
/// Creates a ParamPackage from an SDL_Event that can directly be used to create a ButtonDevice
-static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
+Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event);
+} // Anonymous namespace
static int SDLEventWatcher(void* user_data, SDL_Event* event) {
auto* const sdl_state = static_cast<SDLState*>(user_data);
@@ -48,8 +55,10 @@ static int SDLEventWatcher(void* user_data, SDL_Event* event) {
class SDLJoystick {
public:
- SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick)
- : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose} {}
+ SDLJoystick(std::string guid_, int port_, SDL_Joystick* joystick,
+ SDL_GameController* game_controller)
+ : guid{std::move(guid_)}, port{port_}, sdl_joystick{joystick, &SDL_JoystickClose},
+ sdl_controller{game_controller, &SDL_GameControllerClose} {}
void SetButton(int button, bool value) {
std::lock_guard lock{mutex};
@@ -68,7 +77,17 @@ public:
float GetAxis(int axis, float range) const {
std::lock_guard lock{mutex};
- return state.axes.at(axis) / (32767.0f * range);
+ return static_cast<float>(state.axes.at(axis)) / (32767.0f * range);
+ }
+
+ bool RumblePlay(u16 amp_low, u16 amp_high) {
+ if (sdl_controller) {
+ return SDL_GameControllerRumble(sdl_controller.get(), amp_low, amp_high, 0) == 0;
+ } else if (sdl_joystick) {
+ return SDL_JoystickRumble(sdl_joystick.get(), amp_low, amp_high, 0) == 0;
+ }
+
+ return false;
}
std::tuple<float, float> GetAnalog(int axis_x, int axis_y, float range) const {
@@ -88,6 +107,10 @@ public:
return std::make_tuple(x, y);
}
+ const MotionInput& GetMotion() const {
+ return motion;
+ }
+
void SetHat(int hat, Uint8 direction) {
std::lock_guard lock{mutex};
state.hats.insert_or_assign(hat, direction);
@@ -115,8 +138,13 @@ public:
return sdl_joystick.get();
}
- void SetSDLJoystick(SDL_Joystick* joystick) {
+ SDL_GameController* GetSDLGameController() const {
+ return sdl_controller.get();
+ }
+
+ void SetSDLJoystick(SDL_Joystick* joystick, SDL_GameController* controller) {
sdl_joystick.reset(joystick);
+ sdl_controller.reset(controller);
}
private:
@@ -128,21 +156,29 @@ private:
std::string guid;
int port;
std::unique_ptr<SDL_Joystick, decltype(&SDL_JoystickClose)> sdl_joystick;
+ std::unique_ptr<SDL_GameController, decltype(&SDL_GameControllerClose)> sdl_controller;
mutable std::mutex mutex;
+
+ // Motion is initialized without PID values as motion input is not aviable for SDL2
+ MotionInput motion{0.0f, 0.0f, 0.0f};
};
std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickByGUID(const std::string& guid, int port) {
std::lock_guard lock{joystick_map_mutex};
const auto it = joystick_map.find(guid);
+
if (it != joystick_map.end()) {
while (it->second.size() <= static_cast<std::size_t>(port)) {
- auto joystick =
- std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()), nullptr);
+ auto joystick = std::make_shared<SDLJoystick>(guid, static_cast<int>(it->second.size()),
+ nullptr, nullptr);
it->second.emplace_back(std::move(joystick));
}
- return it->second[port];
+
+ return it->second[static_cast<std::size_t>(port)];
}
- auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr);
+
+ auto joystick = std::make_shared<SDLJoystick>(guid, 0, nullptr, nullptr);
+
return joystick_map[guid].emplace_back(std::move(joystick));
}
@@ -152,86 +188,72 @@ std::shared_ptr<SDLJoystick> SDLState::GetSDLJoystickBySDLID(SDL_JoystickID sdl_
std::lock_guard lock{joystick_map_mutex};
const auto map_it = joystick_map.find(guid);
- if (map_it != joystick_map.end()) {
- const auto vec_it =
- std::find_if(map_it->second.begin(), map_it->second.end(),
- [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
- return sdl_joystick == joystick->GetSDLJoystick();
- });
- if (vec_it != map_it->second.end()) {
- // This is the common case: There is already an existing SDL_Joystick maped to a
- // SDLJoystick. return the SDLJoystick
- return *vec_it;
- }
- // Search for a SDLJoystick without a mapped SDL_Joystick...
- const auto nullptr_it = std::find_if(map_it->second.begin(), map_it->second.end(),
- [](const std::shared_ptr<SDLJoystick>& joystick) {
- return !joystick->GetSDLJoystick();
- });
- if (nullptr_it != map_it->second.end()) {
- // ... and map it
- (*nullptr_it)->SetSDLJoystick(sdl_joystick);
- return *nullptr_it;
- }
+ if (map_it == joystick_map.end()) {
+ return nullptr;
+ }
+
+ const auto vec_it = std::find_if(map_it->second.begin(), map_it->second.end(),
+ [&sdl_joystick](const auto& joystick) {
+ return joystick->GetSDLJoystick() == sdl_joystick;
+ });
- // There is no SDLJoystick without a mapped SDL_Joystick
- // Create a new SDLJoystick
- const int port = static_cast<int>(map_it->second.size());
- auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
- return map_it->second.emplace_back(std::move(joystick));
+ if (vec_it == map_it->second.end()) {
+ return nullptr;
}
- auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
- return joystick_map[guid].emplace_back(std::move(joystick));
+ return *vec_it;
}
void SDLState::InitJoystick(int joystick_index) {
SDL_Joystick* sdl_joystick = SDL_JoystickOpen(joystick_index);
+ SDL_GameController* sdl_gamecontroller = nullptr;
+
+ if (SDL_IsGameController(joystick_index)) {
+ sdl_gamecontroller = SDL_GameControllerOpen(joystick_index);
+ }
+
if (!sdl_joystick) {
- LOG_ERROR(Input, "failed to open joystick {}", joystick_index);
+ LOG_ERROR(Input, "Failed to open joystick {}", joystick_index);
return;
}
+
const std::string guid = GetGUID(sdl_joystick);
std::lock_guard lock{joystick_map_mutex};
if (joystick_map.find(guid) == joystick_map.end()) {
- auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick);
+ auto joystick = std::make_shared<SDLJoystick>(guid, 0, sdl_joystick, sdl_gamecontroller);
joystick_map[guid].emplace_back(std::move(joystick));
return;
}
+
auto& joystick_guid_list = joystick_map[guid];
- const auto it = std::find_if(
- joystick_guid_list.begin(), joystick_guid_list.end(),
- [](const std::shared_ptr<SDLJoystick>& joystick) { return !joystick->GetSDLJoystick(); });
- if (it != joystick_guid_list.end()) {
- (*it)->SetSDLJoystick(sdl_joystick);
+ const auto joystick_it =
+ std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
+ [](const auto& joystick) { return !joystick->GetSDLJoystick(); });
+
+ if (joystick_it != joystick_guid_list.end()) {
+ (*joystick_it)->SetSDLJoystick(sdl_joystick, sdl_gamecontroller);
return;
}
+
const int port = static_cast<int>(joystick_guid_list.size());
- auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick);
+ auto joystick = std::make_shared<SDLJoystick>(guid, port, sdl_joystick, sdl_gamecontroller);
joystick_guid_list.emplace_back(std::move(joystick));
}
void SDLState::CloseJoystick(SDL_Joystick* sdl_joystick) {
const std::string guid = GetGUID(sdl_joystick);
- std::shared_ptr<SDLJoystick> joystick;
- {
- std::lock_guard lock{joystick_map_mutex};
- // This call to guid is safe since the joystick is guaranteed to be in the map
- const auto& joystick_guid_list = joystick_map[guid];
- const auto joystick_it =
- std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
- [&sdl_joystick](const std::shared_ptr<SDLJoystick>& joystick) {
- return joystick->GetSDLJoystick() == sdl_joystick;
- });
- joystick = *joystick_it;
- }
-
- // Destruct SDL_Joystick outside the lock guard because SDL can internally call the
- // event callback which locks the mutex again.
- joystick->SetSDLJoystick(nullptr);
+ std::lock_guard lock{joystick_map_mutex};
+ // This call to guid is safe since the joystick is guaranteed to be in the map
+ const auto& joystick_guid_list = joystick_map[guid];
+ const auto joystick_it = std::find_if(joystick_guid_list.begin(), joystick_guid_list.end(),
+ [&sdl_joystick](const auto& joystick) {
+ return joystick->GetSDLJoystick() == sdl_joystick;
+ });
+
+ (*joystick_it)->SetSDLJoystick(nullptr, nullptr);
}
void SDLState::HandleGameControllerEvent(const SDL_Event& event) {
@@ -329,8 +351,8 @@ private:
class SDLAnalog final : public Input::AnalogDevice {
public:
- SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_, float deadzone_,
- float range_)
+ explicit SDLAnalog(std::shared_ptr<SDLJoystick> joystick_, int axis_x_, int axis_y_,
+ float deadzone_, float range_)
: joystick(std::move(joystick_)), axis_x(axis_x_), axis_y(axis_y_), deadzone(deadzone_),
range(range_) {}
@@ -341,12 +363,12 @@ public:
return std::make_tuple(x / r * (r - deadzone) / (1 - deadzone),
y / r * (r - deadzone) / (1 - deadzone));
}
- return std::make_tuple<float, float>(0.0f, 0.0f);
+ return {};
}
bool GetAnalogDirectionStatus(Input::AnalogDirection direction) const override {
const auto [x, y] = GetStatus();
- const float directional_deadzone = 0.4f;
+ const float directional_deadzone = 0.5f;
switch (direction) {
case Input::AnalogDirection::RIGHT:
return x > directional_deadzone;
@@ -368,6 +390,93 @@ private:
const float range;
};
+class SDLVibration final : public Input::VibrationDevice {
+public:
+ explicit SDLVibration(std::shared_ptr<SDLJoystick> joystick_)
+ : joystick(std::move(joystick_)) {}
+
+ u8 GetStatus() const override {
+ joystick->RumblePlay(1, 1);
+ return joystick->RumblePlay(0, 0);
+ }
+
+ bool SetRumblePlay(f32 amp_low, f32 freq_low, f32 amp_high, f32 freq_high) const override {
+ const auto process_amplitude = [](f32 amplitude) {
+ return static_cast<u16>((amplitude + std::pow(amplitude, 0.3f)) * 0.5f * 0xFFFF);
+ };
+
+ const auto processed_amp_low = process_amplitude(amp_low);
+ const auto processed_amp_high = process_amplitude(amp_high);
+
+ return joystick->RumblePlay(processed_amp_low, processed_amp_high);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+};
+
+class SDLDirectionMotion final : public Input::MotionDevice {
+public:
+ explicit SDLDirectionMotion(std::shared_ptr<SDLJoystick> joystick_, int hat_, Uint8 direction_)
+ : joystick(std::move(joystick_)), hat(hat_), direction(direction_) {}
+
+ Input::MotionStatus GetStatus() const override {
+ if (joystick->GetHatDirection(hat, direction)) {
+ return joystick->GetMotion().GetRandomMotion(2, 6);
+ }
+ return joystick->GetMotion().GetRandomMotion(0, 0);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+ int hat;
+ Uint8 direction;
+};
+
+class SDLAxisMotion final : public Input::MotionDevice {
+public:
+ explicit SDLAxisMotion(std::shared_ptr<SDLJoystick> joystick_, int axis_, float threshold_,
+ bool trigger_if_greater_)
+ : joystick(std::move(joystick_)), axis(axis_), threshold(threshold_),
+ trigger_if_greater(trigger_if_greater_) {}
+
+ Input::MotionStatus GetStatus() const override {
+ const float axis_value = joystick->GetAxis(axis, 1.0f);
+ bool trigger = axis_value < threshold;
+ if (trigger_if_greater) {
+ trigger = axis_value > threshold;
+ }
+
+ if (trigger) {
+ return joystick->GetMotion().GetRandomMotion(2, 6);
+ }
+ return joystick->GetMotion().GetRandomMotion(0, 0);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+ int axis;
+ float threshold;
+ bool trigger_if_greater;
+};
+
+class SDLButtonMotion final : public Input::MotionDevice {
+public:
+ explicit SDLButtonMotion(std::shared_ptr<SDLJoystick> joystick_, int button_)
+ : joystick(std::move(joystick_)), button(button_) {}
+
+ Input::MotionStatus GetStatus() const override {
+ if (joystick->GetButton(button)) {
+ return joystick->GetMotion().GetRandomMotion(2, 6);
+ }
+ return joystick->GetMotion().GetRandomMotion(0, 0);
+ }
+
+private:
+ std::shared_ptr<SDLJoystick> joystick;
+ int button;
+};
+
/// A button device factory that creates button devices from SDL joystick
class SDLButtonFactory final : public Input::Factory<Input::ButtonDevice> {
public:
@@ -448,7 +557,7 @@ class SDLAnalogFactory final : public Input::Factory<Input::AnalogDevice> {
public:
explicit SDLAnalogFactory(SDLState& state_) : state(state_) {}
/**
- * Creates analog device from joystick axes
+ * Creates an analog device from joystick axes
* @param params contains parameters for creating the device:
* - "guid": the guid of the joystick to bind
* - "port": the nth joystick of the same type
@@ -460,7 +569,7 @@ public:
const int port = params.Get("port", 0);
const int axis_x = params.Get("axis_x", 0);
const int axis_y = params.Get("axis_y", 1);
- const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, .99f);
+ const float deadzone = std::clamp(params.Get("deadzone", 0.0f), 0.0f, 1.0f);
const float range = std::clamp(params.Get("range", 1.0f), 0.50f, 1.50f);
auto joystick = state.GetSDLJoystickByGUID(guid, port);
@@ -474,17 +583,108 @@ private:
SDLState& state;
};
+/// An vibration device factory that creates vibration devices from SDL joystick
+class SDLVibrationFactory final : public Input::Factory<Input::VibrationDevice> {
+public:
+ explicit SDLVibrationFactory(SDLState& state_) : state(state_) {}
+ /**
+ * Creates a vibration device from a joystick
+ * @param params contains parameters for creating the device:
+ * - "guid": the guid of the joystick to bind
+ * - "port": the nth joystick of the same type
+ */
+ std::unique_ptr<Input::VibrationDevice> Create(const Common::ParamPackage& params) override {
+ const std::string guid = params.Get("guid", "0");
+ const int port = params.Get("port", 0);
+ return std::make_unique<SDLVibration>(state.GetSDLJoystickByGUID(guid, port));
+ }
+
+private:
+ SDLState& state;
+};
+
+/// A motion device factory that creates motion devices from SDL joystick
+class SDLMotionFactory final : public Input::Factory<Input::MotionDevice> {
+public:
+ explicit SDLMotionFactory(SDLState& state_) : state(state_) {}
+ /**
+ * Creates motion device from joystick axes
+ * @param params contains parameters for creating the device:
+ * - "guid": the guid of the joystick to bind
+ * - "port": the nth joystick of the same type
+ */
+ std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
+ const std::string guid = params.Get("guid", "0");
+ const int port = params.Get("port", 0);
+
+ auto joystick = state.GetSDLJoystickByGUID(guid, port);
+
+ if (params.Has("hat")) {
+ const int hat = params.Get("hat", 0);
+ const std::string direction_name = params.Get("direction", "");
+ Uint8 direction;
+ if (direction_name == "up") {
+ direction = SDL_HAT_UP;
+ } else if (direction_name == "down") {
+ direction = SDL_HAT_DOWN;
+ } else if (direction_name == "left") {
+ direction = SDL_HAT_LEFT;
+ } else if (direction_name == "right") {
+ direction = SDL_HAT_RIGHT;
+ } else {
+ direction = 0;
+ }
+ // This is necessary so accessing GetHat with hat won't crash
+ joystick->SetHat(hat, SDL_HAT_CENTERED);
+ return std::make_unique<SDLDirectionMotion>(joystick, hat, direction);
+ }
+
+ if (params.Has("axis")) {
+ const int axis = params.Get("axis", 0);
+ const float threshold = params.Get("threshold", 0.5f);
+ const std::string direction_name = params.Get("direction", "");
+ bool trigger_if_greater;
+ if (direction_name == "+") {
+ trigger_if_greater = true;
+ } else if (direction_name == "-") {
+ trigger_if_greater = false;
+ } else {
+ trigger_if_greater = true;
+ LOG_ERROR(Input, "Unknown direction {}", direction_name);
+ }
+ // This is necessary so accessing GetAxis with axis won't crash
+ joystick->SetAxis(axis, 0);
+ return std::make_unique<SDLAxisMotion>(joystick, axis, threshold, trigger_if_greater);
+ }
+
+ const int button = params.Get("button", 0);
+ // This is necessary so accessing GetButton with button won't crash
+ joystick->SetButton(button, false);
+ return std::make_unique<SDLButtonMotion>(joystick, button);
+ }
+
+private:
+ SDLState& state;
+};
+
SDLState::SDLState() {
using namespace Input;
- RegisterFactory<ButtonDevice>("sdl", std::make_shared<SDLButtonFactory>(*this));
- RegisterFactory<AnalogDevice>("sdl", std::make_shared<SDLAnalogFactory>(*this));
-
- // If the frontend is going to manage the event loop, then we dont start one here
- start_thread = !SDL_WasInit(SDL_INIT_JOYSTICK);
+ button_factory = std::make_shared<SDLButtonFactory>(*this);
+ analog_factory = std::make_shared<SDLAnalogFactory>(*this);
+ vibration_factory = std::make_shared<SDLVibrationFactory>(*this);
+ motion_factory = std::make_shared<SDLMotionFactory>(*this);
+ RegisterFactory<ButtonDevice>("sdl", button_factory);
+ RegisterFactory<AnalogDevice>("sdl", analog_factory);
+ RegisterFactory<VibrationDevice>("sdl", vibration_factory);
+ RegisterFactory<MotionDevice>("sdl", motion_factory);
+
+ // If the frontend is going to manage the event loop, then we don't start one here
+ start_thread = SDL_WasInit(SDL_INIT_JOYSTICK) == 0;
if (start_thread && SDL_Init(SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Input, "SDL_Init(SDL_INIT_JOYSTICK) failed with: {}", SDL_GetError());
return;
}
+ has_gamecontroller = SDL_InitSubSystem(SDL_INIT_GAMECONTROLLER) != 0;
if (SDL_SetHint(SDL_HINT_JOYSTICK_ALLOW_BACKGROUND_EVENTS, "1") == SDL_FALSE) {
LOG_ERROR(Input, "Failed to set hint for background events with: {}", SDL_GetError());
}
@@ -497,7 +697,7 @@ SDLState::SDLState() {
using namespace std::chrono_literals;
while (initialized) {
SDL_PumpEvents();
- std::this_thread::sleep_for(10ms);
+ std::this_thread::sleep_for(1ms);
}
});
}
@@ -512,6 +712,8 @@ SDLState::~SDLState() {
using namespace Input;
UnregisterFactory<ButtonDevice>("sdl");
UnregisterFactory<AnalogDevice>("sdl");
+ UnregisterFactory<VibrationDevice>("sdl");
+ UnregisterFactory<MotionDevice>("sdl");
CloseJoysticks();
SDL_DelEventWatch(&SDLEventWatcher, this);
@@ -523,65 +725,266 @@ SDLState::~SDLState() {
}
}
-static Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
+std::vector<Common::ParamPackage> SDLState::GetInputDevices() {
+ std::scoped_lock lock(joystick_map_mutex);
+ std::vector<Common::ParamPackage> devices;
+ for (const auto& [key, value] : joystick_map) {
+ for (const auto& joystick : value) {
+ if (auto* const controller = joystick->GetSDLGameController()) {
+ std::string name =
+ fmt::format("{} {}", SDL_GameControllerName(controller), joystick->GetPort());
+ devices.emplace_back(Common::ParamPackage{
+ {"class", "sdl"},
+ {"display", std::move(name)},
+ {"guid", joystick->GetGUID()},
+ {"port", std::to_string(joystick->GetPort())},
+ });
+ } else if (auto* const joy = joystick->GetSDLJoystick()) {
+ std::string name = fmt::format("{} {}", SDL_JoystickName(joy), joystick->GetPort());
+ devices.emplace_back(Common::ParamPackage{
+ {"class", "sdl"},
+ {"display", std::move(name)},
+ {"guid", joystick->GetGUID()},
+ {"port", std::to_string(joystick->GetPort())},
+ });
+ }
+ }
+ }
+ return devices;
+}
+
+namespace {
+Common::ParamPackage BuildAnalogParamPackageForButton(int port, std::string guid, s32 axis,
+ float value = 0.1f) {
+ Common::ParamPackage params({{"engine", "sdl"}});
+ params.Set("port", port);
+ params.Set("guid", std::move(guid));
+ params.Set("axis", axis);
+ if (value > 0) {
+ params.Set("direction", "+");
+ params.Set("threshold", "0.5");
+ } else {
+ params.Set("direction", "-");
+ params.Set("threshold", "-0.5");
+ }
+ return params;
+}
+
+Common::ParamPackage BuildButtonParamPackageForButton(int port, std::string guid, s32 button) {
+ Common::ParamPackage params({{"engine", "sdl"}});
+ params.Set("port", port);
+ params.Set("guid", std::move(guid));
+ params.Set("button", button);
+ return params;
+}
+
+Common::ParamPackage BuildHatParamPackageForButton(int port, std::string guid, s32 hat, s32 value) {
Common::ParamPackage params({{"engine", "sdl"}});
+ params.Set("port", port);
+ params.Set("guid", std::move(guid));
+ params.Set("hat", hat);
+ switch (value) {
+ case SDL_HAT_UP:
+ params.Set("direction", "up");
+ break;
+ case SDL_HAT_DOWN:
+ params.Set("direction", "down");
+ break;
+ case SDL_HAT_LEFT:
+ params.Set("direction", "left");
+ break;
+ case SDL_HAT_RIGHT:
+ params.Set("direction", "right");
+ break;
+ default:
+ return {};
+ }
+ return params;
+}
+
+Common::ParamPackage SDLEventToButtonParamPackage(SDLState& state, const SDL_Event& event) {
switch (event.type) {
case SDL_JOYAXISMOTION: {
- const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("axis", event.jaxis.axis);
- if (event.jaxis.value > 0) {
- params.Set("direction", "+");
- params.Set("threshold", "0.5");
- } else {
- params.Set("direction", "-");
- params.Set("threshold", "-0.5");
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
+ return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ static_cast<s32>(event.jaxis.axis),
+ event.jaxis.value);
}
break;
}
case SDL_JOYBUTTONUP: {
- const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which);
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("button", event.jbutton.button);
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
+ return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ static_cast<s32>(event.jbutton.button));
+ }
break;
}
case SDL_JOYHATMOTION: {
- const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which);
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("hat", event.jhat.hat);
- switch (event.jhat.value) {
- case SDL_HAT_UP:
- params.Set("direction", "up");
- break;
- case SDL_HAT_DOWN:
- params.Set("direction", "down");
- break;
- case SDL_HAT_LEFT:
- params.Set("direction", "left");
- break;
- case SDL_HAT_RIGHT:
- params.Set("direction", "right");
- break;
- default:
- return {};
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
+ return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ static_cast<s32>(event.jhat.hat),
+ static_cast<s32>(event.jhat.value));
}
break;
}
}
+ return {};
+}
+
+Common::ParamPackage SDLEventToMotionParamPackage(SDLState& state, const SDL_Event& event) {
+ switch (event.type) {
+ case SDL_JOYAXISMOTION: {
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
+ return BuildAnalogParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ static_cast<s32>(event.jaxis.axis),
+ event.jaxis.value);
+ }
+ break;
+ }
+ case SDL_JOYBUTTONUP: {
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.jbutton.which)) {
+ return BuildButtonParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ static_cast<s32>(event.jbutton.button));
+ }
+ break;
+ }
+ case SDL_JOYHATMOTION: {
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.jhat.which)) {
+ return BuildHatParamPackageForButton(joystick->GetPort(), joystick->GetGUID(),
+ static_cast<s32>(event.jhat.hat),
+ static_cast<s32>(event.jhat.value));
+ }
+ break;
+ }
+ }
+ return {};
+}
+
+Common::ParamPackage BuildParamPackageForBinding(int port, const std::string& guid,
+ const SDL_GameControllerButtonBind& binding) {
+ switch (binding.bindType) {
+ case SDL_CONTROLLER_BINDTYPE_AXIS:
+ return BuildAnalogParamPackageForButton(port, guid, binding.value.axis);
+ case SDL_CONTROLLER_BINDTYPE_BUTTON:
+ return BuildButtonParamPackageForButton(port, guid, binding.value.button);
+ case SDL_CONTROLLER_BINDTYPE_HAT:
+ return BuildHatParamPackageForButton(port, guid, binding.value.hat.hat,
+ binding.value.hat.hat_mask);
+ }
+ return {};
+}
+
+Common::ParamPackage BuildParamPackageForAnalog(int port, const std::string& guid, int axis_x,
+ int axis_y) {
+ Common::ParamPackage params;
+ params.Set("engine", "sdl");
+ params.Set("port", port);
+ params.Set("guid", guid);
+ params.Set("axis_x", axis_x);
+ params.Set("axis_y", axis_y);
return params;
}
+} // Anonymous namespace
-namespace Polling {
+ButtonMapping SDLState::GetButtonMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return {};
+ }
+ const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
+ auto* controller = joystick->GetSDLGameController();
+ if (controller == nullptr) {
+ return {};
+ }
+
+ // This list is missing ZL/ZR since those are not considered buttons in SDL GameController.
+ // We will add those afterwards
+ // This list also excludes Screenshot since theres not really a mapping for that
+ using ButtonBindings =
+ std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerButton>, 17>;
+ static constexpr ButtonBindings switch_to_sdl_button{{
+ {Settings::NativeButton::A, SDL_CONTROLLER_BUTTON_B},
+ {Settings::NativeButton::B, SDL_CONTROLLER_BUTTON_A},
+ {Settings::NativeButton::X, SDL_CONTROLLER_BUTTON_Y},
+ {Settings::NativeButton::Y, SDL_CONTROLLER_BUTTON_X},
+ {Settings::NativeButton::LStick, SDL_CONTROLLER_BUTTON_LEFTSTICK},
+ {Settings::NativeButton::RStick, SDL_CONTROLLER_BUTTON_RIGHTSTICK},
+ {Settings::NativeButton::L, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
+ {Settings::NativeButton::R, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
+ {Settings::NativeButton::Plus, SDL_CONTROLLER_BUTTON_START},
+ {Settings::NativeButton::Minus, SDL_CONTROLLER_BUTTON_BACK},
+ {Settings::NativeButton::DLeft, SDL_CONTROLLER_BUTTON_DPAD_LEFT},
+ {Settings::NativeButton::DUp, SDL_CONTROLLER_BUTTON_DPAD_UP},
+ {Settings::NativeButton::DRight, SDL_CONTROLLER_BUTTON_DPAD_RIGHT},
+ {Settings::NativeButton::DDown, SDL_CONTROLLER_BUTTON_DPAD_DOWN},
+ {Settings::NativeButton::SL, SDL_CONTROLLER_BUTTON_LEFTSHOULDER},
+ {Settings::NativeButton::SR, SDL_CONTROLLER_BUTTON_RIGHTSHOULDER},
+ {Settings::NativeButton::Home, SDL_CONTROLLER_BUTTON_GUIDE},
+ }};
+
+ // Add the missing bindings for ZL/ZR
+ using ZBindings =
+ std::array<std::pair<Settings::NativeButton::Values, SDL_GameControllerAxis>, 2>;
+ static constexpr ZBindings switch_to_sdl_axis{{
+ {Settings::NativeButton::ZL, SDL_CONTROLLER_AXIS_TRIGGERLEFT},
+ {Settings::NativeButton::ZR, SDL_CONTROLLER_AXIS_TRIGGERRIGHT},
+ }};
+
+ ButtonMapping mapping;
+ mapping.reserve(switch_to_sdl_button.size() + switch_to_sdl_axis.size());
+
+ for (const auto& [switch_button, sdl_button] : switch_to_sdl_button) {
+ const auto& binding = SDL_GameControllerGetBindForButton(controller, sdl_button);
+ mapping.insert_or_assign(
+ switch_button,
+ BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
+ }
+ for (const auto& [switch_button, sdl_axis] : switch_to_sdl_axis) {
+ const auto& binding = SDL_GameControllerGetBindForAxis(controller, sdl_axis);
+ mapping.insert_or_assign(
+ switch_button,
+ BuildParamPackageForBinding(joystick->GetPort(), joystick->GetGUID(), binding));
+ }
+
+ return mapping;
+}
+
+AnalogMapping SDLState::GetAnalogMappingForDevice(const Common::ParamPackage& params) {
+ if (!params.Has("guid") || !params.Has("port")) {
+ return {};
+ }
+ const auto joystick = GetSDLJoystickByGUID(params.Get("guid", ""), params.Get("port", 0));
+ auto* controller = joystick->GetSDLGameController();
+ if (controller == nullptr) {
+ return {};
+ }
+
+ AnalogMapping mapping = {};
+ const auto& binding_left_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTX);
+ const auto& binding_left_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_LEFTY);
+ mapping.insert_or_assign(Settings::NativeAnalog::LStick,
+ BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
+ binding_left_x.value.axis,
+ binding_left_y.value.axis));
+ const auto& binding_right_x =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTX);
+ const auto& binding_right_y =
+ SDL_GameControllerGetBindForAxis(controller, SDL_CONTROLLER_AXIS_RIGHTY);
+ mapping.insert_or_assign(Settings::NativeAnalog::RStick,
+ BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
+ binding_right_x.value.axis,
+ binding_right_y.value.axis));
+ return mapping;
+}
+namespace Polling {
class SDLPoller : public InputCommon::Polling::DevicePoller {
public:
explicit SDLPoller(SDLState& state_) : state(state_) {}
- void Start() override {
+ void Start(const std::string& device_id) override {
state.event_queue.Clear();
state.polling = true;
}
@@ -601,71 +1004,116 @@ public:
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
- switch (event.type) {
- case SDL_JOYAXISMOTION:
- if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
- break;
- }
- [[fallthrough]];
- case SDL_JOYBUTTONUP:
- case SDL_JOYHATMOTION:
- return SDLEventToButtonParamPackage(state, event);
+ const auto package = FromEvent(event);
+ if (package) {
+ return *package;
}
}
return {};
}
+ [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
+ switch (event.type) {
+ case SDL_JOYAXISMOTION:
+ if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
+ break;
+ }
+ [[fallthrough]];
+ case SDL_JOYBUTTONUP:
+ case SDL_JOYHATMOTION:
+ return {SDLEventToButtonParamPackage(state, event)};
+ }
+ return std::nullopt;
+ }
};
-class SDLAnalogPoller final : public SDLPoller {
+class SDLMotionPoller final : public SDLPoller {
public:
- explicit SDLAnalogPoller(SDLState& state_) : SDLPoller(state_) {}
+ explicit SDLMotionPoller(SDLState& state_) : SDLPoller(state_) {}
+
+ Common::ParamPackage GetNextInput() override {
+ SDL_Event event;
+ while (state.event_queue.Pop(event)) {
+ const auto package = FromEvent(event);
+ if (package) {
+ return *package;
+ }
+ }
+ return {};
+ }
+ [[nodiscard]] std::optional<Common::ParamPackage> FromEvent(const SDL_Event& event) const {
+ switch (event.type) {
+ case SDL_JOYAXISMOTION:
+ if (std::abs(event.jaxis.value / 32767.0) < 0.5) {
+ break;
+ }
+ [[fallthrough]];
+ case SDL_JOYBUTTONUP:
+ case SDL_JOYHATMOTION:
+ return {SDLEventToMotionParamPackage(state, event)};
+ }
+ return std::nullopt;
+ }
+};
- void Start() override {
- SDLPoller::Start();
+/**
+ * Attempts to match the press to a controller joy axis (left/right stick) and if a match
+ * isn't found, checks if the event matches anything from SDLButtonPoller and uses that
+ * instead
+ */
+class SDLAnalogPreferredPoller final : public SDLPoller {
+public:
+ explicit SDLAnalogPreferredPoller(SDLState& state_)
+ : SDLPoller(state_), button_poller(state_) {}
+ void Start(const std::string& device_id) override {
+ SDLPoller::Start(device_id);
// Reset stored axes
analog_x_axis = -1;
analog_y_axis = -1;
- analog_axes_joystick = -1;
}
Common::ParamPackage GetNextInput() override {
SDL_Event event;
while (state.event_queue.Pop(event)) {
- if (event.type != SDL_JOYAXISMOTION || std::abs(event.jaxis.value / 32767.0) < 0.5) {
+ // Filter out axis events that are below a threshold
+ if (event.type == SDL_JOYAXISMOTION && std::abs(event.jaxis.value / 32767.0) < 0.5) {
continue;
}
- // An analog device needs two axes, so we need to store the axis for later and wait for
- // a second SDL event. The axes also must be from the same joystick.
- const int axis = event.jaxis.axis;
- if (analog_x_axis == -1) {
- analog_x_axis = axis;
- analog_axes_joystick = event.jaxis.which;
- } else if (analog_y_axis == -1 && analog_x_axis != axis &&
- analog_axes_joystick == event.jaxis.which) {
- analog_y_axis = axis;
+ if (event.type == SDL_JOYAXISMOTION) {
+ const auto axis = event.jaxis.axis;
+ // In order to return a complete analog param, we need inputs for both axes.
+ // First we take the x-axis (horizontal) input, then the y-axis (vertical) input.
+ if (analog_x_axis == -1) {
+ analog_x_axis = axis;
+ } else if (analog_y_axis == -1 && analog_x_axis != axis) {
+ analog_y_axis = axis;
+ }
+ } else {
+ // If the press wasn't accepted as a joy axis, check for a button press
+ auto button_press = button_poller.FromEvent(event);
+ if (button_press) {
+ return *button_press;
+ }
}
}
- Common::ParamPackage params;
+
if (analog_x_axis != -1 && analog_y_axis != -1) {
- const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which);
- params.Set("engine", "sdl");
- params.Set("port", joystick->GetPort());
- params.Set("guid", joystick->GetGUID());
- params.Set("axis_x", analog_x_axis);
- params.Set("axis_y", analog_y_axis);
- analog_x_axis = -1;
- analog_y_axis = -1;
- analog_axes_joystick = -1;
- return params;
+ if (const auto joystick = state.GetSDLJoystickBySDLID(event.jaxis.which)) {
+ auto params = BuildParamPackageForAnalog(joystick->GetPort(), joystick->GetGUID(),
+ analog_x_axis, analog_y_axis);
+ analog_x_axis = -1;
+ analog_y_axis = -1;
+ return params;
+ }
}
- return params;
+
+ return {};
}
private:
int analog_x_axis = -1;
int analog_y_axis = -1;
- SDL_JoystickID analog_axes_joystick = -1;
+ SDLButtonPoller button_poller;
};
} // namespace Polling
@@ -673,12 +1121,15 @@ SDLState::Pollers SDLState::GetPollers(InputCommon::Polling::DeviceType type) {
Pollers pollers;
switch (type) {
- case InputCommon::Polling::DeviceType::Analog:
- pollers.emplace_back(std::make_unique<Polling::SDLAnalogPoller>(*this));
+ case InputCommon::Polling::DeviceType::AnalogPreferred:
+ pollers.emplace_back(std::make_unique<Polling::SDLAnalogPreferredPoller>(*this));
break;
case InputCommon::Polling::DeviceType::Button:
pollers.emplace_back(std::make_unique<Polling::SDLButtonPoller>(*this));
break;
+ case InputCommon::Polling::DeviceType::Motion:
+ pollers.emplace_back(std::make_unique<Polling::SDLMotionPoller>(*this));
+ break;
}
return pollers;
diff --git a/src/input_common/sdl/sdl_impl.h b/src/input_common/sdl/sdl_impl.h
index 606a32c5b..08044b00d 100644
--- a/src/input_common/sdl/sdl_impl.h
+++ b/src/input_common/sdl/sdl_impl.h
@@ -21,6 +21,8 @@ namespace InputCommon::SDL {
class SDLAnalogFactory;
class SDLButtonFactory;
+class SDLMotionFactory;
+class SDLVibrationFactory;
class SDLJoystick;
class SDLState : public State {
@@ -50,6 +52,11 @@ public:
std::atomic<bool> polling = false;
Common::SPSCQueue<SDL_Event> event_queue;
+ std::vector<Common::ParamPackage> GetInputDevices() override;
+
+ ButtonMapping GetButtonMappingForDevice(const Common::ParamPackage& params) override;
+ AnalogMapping GetAnalogMappingForDevice(const Common::ParamPackage& params) override;
+
private:
void InitJoystick(int joystick_index);
void CloseJoystick(SDL_Joystick* sdl_joystick);
@@ -57,12 +64,17 @@ private:
/// Needs to be called before SDL_QuitSubSystem.
void CloseJoysticks();
+ // Set to true if SDL supports game controller subsystem
+ bool has_gamecontroller = false;
+
/// Map of GUID of a list of corresponding virtual Joysticks
std::unordered_map<std::string, std::vector<std::shared_ptr<SDLJoystick>>> joystick_map;
std::mutex joystick_map_mutex;
std::shared_ptr<SDLButtonFactory> button_factory;
std::shared_ptr<SDLAnalogFactory> analog_factory;
+ std::shared_ptr<SDLVibrationFactory> vibration_factory;
+ std::shared_ptr<SDLMotionFactory> motion_factory;
bool start_thread = false;
std::atomic<bool> initialized = false;
diff --git a/src/input_common/settings.cpp b/src/input_common/settings.cpp
new file mode 100644
index 000000000..557e7a9a0
--- /dev/null
+++ b/src/input_common/settings.cpp
@@ -0,0 +1,47 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "input_common/settings.h"
+
+namespace Settings {
+namespace NativeButton {
+const std::array<const char*, NumButtons> mapping = {{
+ "button_a", "button_b", "button_x", "button_y", "button_lstick",
+ "button_rstick", "button_l", "button_r", "button_zl", "button_zr",
+ "button_plus", "button_minus", "button_dleft", "button_dup", "button_dright",
+ "button_ddown", "button_sl", "button_sr", "button_home", "button_screenshot",
+}};
+}
+
+namespace NativeAnalog {
+const std::array<const char*, NumAnalogs> mapping = {{
+ "lstick",
+ "rstick",
+}};
+}
+
+namespace NativeVibration {
+const std::array<const char*, NumVibrations> mapping = {{
+ "left_vibration_device",
+ "right_vibration_device",
+}};
+}
+
+namespace NativeMotion {
+const std::array<const char*, NumMotions> mapping = {{
+ "motionleft",
+ "motionright",
+}};
+}
+
+namespace NativeMouseButton {
+const std::array<const char*, NumMouseButtons> mapping = {{
+ "left",
+ "right",
+ "middle",
+ "forward",
+ "back",
+}};
+}
+} // namespace Settings
diff --git a/src/input_common/settings.h b/src/input_common/settings.h
new file mode 100644
index 000000000..75486554b
--- /dev/null
+++ b/src/input_common/settings.h
@@ -0,0 +1,371 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <string>
+#include "common/common_types.h"
+
+namespace Settings {
+namespace NativeButton {
+enum Values : int {
+ A,
+ B,
+ X,
+ Y,
+ LStick,
+ RStick,
+ L,
+ R,
+ ZL,
+ ZR,
+ Plus,
+ Minus,
+
+ DLeft,
+ DUp,
+ DRight,
+ DDown,
+
+ SL,
+ SR,
+
+ Home,
+ Screenshot,
+
+ NumButtons,
+};
+
+constexpr int BUTTON_HID_BEGIN = A;
+constexpr int BUTTON_NS_BEGIN = Home;
+
+constexpr int BUTTON_HID_END = BUTTON_NS_BEGIN;
+constexpr int BUTTON_NS_END = NumButtons;
+
+constexpr int NUM_BUTTONS_HID = BUTTON_HID_END - BUTTON_HID_BEGIN;
+constexpr int NUM_BUTTONS_NS = BUTTON_NS_END - BUTTON_NS_BEGIN;
+
+extern const std::array<const char*, NumButtons> mapping;
+
+} // namespace NativeButton
+
+namespace NativeAnalog {
+enum Values : int {
+ LStick,
+ RStick,
+
+ NumAnalogs,
+};
+
+constexpr int STICK_HID_BEGIN = LStick;
+constexpr int STICK_HID_END = NumAnalogs;
+constexpr int NUM_STICKS_HID = NumAnalogs;
+
+extern const std::array<const char*, NumAnalogs> mapping;
+} // namespace NativeAnalog
+
+namespace NativeVibration {
+enum Values : int {
+ LeftVibrationDevice,
+ RightVibrationDevice,
+
+ NumVibrations,
+};
+
+constexpr int VIBRATION_HID_BEGIN = LeftVibrationDevice;
+constexpr int VIBRATION_HID_END = NumVibrations;
+constexpr int NUM_VIBRATIONS_HID = NumVibrations;
+
+extern const std::array<const char*, NumVibrations> mapping;
+}; // namespace NativeVibration
+
+namespace NativeMotion {
+enum Values : int {
+ MotionLeft,
+ MotionRight,
+
+ NumMotions,
+};
+
+constexpr int MOTION_HID_BEGIN = MotionLeft;
+constexpr int MOTION_HID_END = NumMotions;
+constexpr int NUM_MOTIONS_HID = NumMotions;
+
+extern const std::array<const char*, NumMotions> mapping;
+} // namespace NativeMotion
+
+namespace NativeMouseButton {
+enum Values {
+ Left,
+ Right,
+ Middle,
+ Forward,
+ Back,
+
+ NumMouseButtons,
+};
+
+constexpr int MOUSE_HID_BEGIN = Left;
+constexpr int MOUSE_HID_END = NumMouseButtons;
+constexpr int NUM_MOUSE_HID = NumMouseButtons;
+
+extern const std::array<const char*, NumMouseButtons> mapping;
+} // namespace NativeMouseButton
+
+namespace NativeKeyboard {
+enum Keys {
+ None,
+ Error,
+
+ A = 4,
+ B,
+ C,
+ D,
+ E,
+ F,
+ G,
+ H,
+ I,
+ J,
+ K,
+ L,
+ M,
+ N,
+ O,
+ P,
+ Q,
+ R,
+ S,
+ T,
+ U,
+ V,
+ W,
+ X,
+ Y,
+ Z,
+ N1,
+ N2,
+ N3,
+ N4,
+ N5,
+ N6,
+ N7,
+ N8,
+ N9,
+ N0,
+ Enter,
+ Escape,
+ Backspace,
+ Tab,
+ Space,
+ Minus,
+ Equal,
+ LeftBrace,
+ RightBrace,
+ Backslash,
+ Tilde,
+ Semicolon,
+ Apostrophe,
+ Grave,
+ Comma,
+ Dot,
+ Slash,
+ CapsLockKey,
+
+ F1,
+ F2,
+ F3,
+ F4,
+ F5,
+ F6,
+ F7,
+ F8,
+ F9,
+ F10,
+ F11,
+ F12,
+
+ SystemRequest,
+ ScrollLockKey,
+ Pause,
+ Insert,
+ Home,
+ PageUp,
+ Delete,
+ End,
+ PageDown,
+ Right,
+ Left,
+ Down,
+ Up,
+
+ NumLockKey,
+ KPSlash,
+ KPAsterisk,
+ KPMinus,
+ KPPlus,
+ KPEnter,
+ KP1,
+ KP2,
+ KP3,
+ KP4,
+ KP5,
+ KP6,
+ KP7,
+ KP8,
+ KP9,
+ KP0,
+ KPDot,
+
+ Key102,
+ Compose,
+ Power,
+ KPEqual,
+
+ F13,
+ F14,
+ F15,
+ F16,
+ F17,
+ F18,
+ F19,
+ F20,
+ F21,
+ F22,
+ F23,
+ F24,
+
+ Open,
+ Help,
+ Properties,
+ Front,
+ Stop,
+ Repeat,
+ Undo,
+ Cut,
+ Copy,
+ Paste,
+ Find,
+ Mute,
+ VolumeUp,
+ VolumeDown,
+ CapsLockActive,
+ NumLockActive,
+ ScrollLockActive,
+ KPComma,
+
+ KPLeftParenthesis,
+ KPRightParenthesis,
+
+ LeftControlKey = 0xE0,
+ LeftShiftKey,
+ LeftAltKey,
+ LeftMetaKey,
+ RightControlKey,
+ RightShiftKey,
+ RightAltKey,
+ RightMetaKey,
+
+ MediaPlayPause,
+ MediaStopCD,
+ MediaPrevious,
+ MediaNext,
+ MediaEject,
+ MediaVolumeUp,
+ MediaVolumeDown,
+ MediaMute,
+ MediaWebsite,
+ MediaBack,
+ MediaForward,
+ MediaStop,
+ MediaFind,
+ MediaScrollUp,
+ MediaScrollDown,
+ MediaEdit,
+ MediaSleep,
+ MediaCoffee,
+ MediaRefresh,
+ MediaCalculator,
+
+ NumKeyboardKeys,
+};
+
+static_assert(NumKeyboardKeys == 0xFC, "Incorrect number of keyboard keys.");
+
+enum Modifiers {
+ LeftControl,
+ LeftShift,
+ LeftAlt,
+ LeftMeta,
+ RightControl,
+ RightShift,
+ RightAlt,
+ RightMeta,
+ CapsLock,
+ ScrollLock,
+ NumLock,
+
+ NumKeyboardMods,
+};
+
+constexpr int KEYBOARD_KEYS_HID_BEGIN = None;
+constexpr int KEYBOARD_KEYS_HID_END = NumKeyboardKeys;
+constexpr int NUM_KEYBOARD_KEYS_HID = NumKeyboardKeys;
+
+constexpr int KEYBOARD_MODS_HID_BEGIN = LeftControl;
+constexpr int KEYBOARD_MODS_HID_END = NumKeyboardMods;
+constexpr int NUM_KEYBOARD_MODS_HID = NumKeyboardMods;
+
+} // namespace NativeKeyboard
+
+using AnalogsRaw = std::array<std::string, NativeAnalog::NumAnalogs>;
+using ButtonsRaw = std::array<std::string, NativeButton::NumButtons>;
+using MotionsRaw = std::array<std::string, NativeMotion::NumMotions>;
+using VibrationsRaw = std::array<std::string, NativeVibration::NumVibrations>;
+
+using MouseButtonsRaw = std::array<std::string, NativeMouseButton::NumMouseButtons>;
+using KeyboardKeysRaw = std::array<std::string, NativeKeyboard::NumKeyboardKeys>;
+using KeyboardModsRaw = std::array<std::string, NativeKeyboard::NumKeyboardMods>;
+
+constexpr u32 JOYCON_BODY_NEON_RED = 0xFF3C28;
+constexpr u32 JOYCON_BUTTONS_NEON_RED = 0x1E0A0A;
+constexpr u32 JOYCON_BODY_NEON_BLUE = 0x0AB9E6;
+constexpr u32 JOYCON_BUTTONS_NEON_BLUE = 0x001E1E;
+
+enum class ControllerType {
+ ProController,
+ DualJoyconDetached,
+ LeftJoycon,
+ RightJoycon,
+ Handheld,
+};
+
+struct PlayerInput {
+ bool connected;
+ ControllerType controller_type;
+ ButtonsRaw buttons;
+ AnalogsRaw analogs;
+ VibrationsRaw vibrations;
+ MotionsRaw motions;
+
+ bool vibration_enabled;
+ int vibration_strength;
+
+ u32 body_color_left;
+ u32 body_color_right;
+ u32 button_color_left;
+ u32 button_color_right;
+};
+
+struct TouchscreenInput {
+ bool enabled;
+ std::string device;
+
+ u32 finger;
+ u32 diameter_x;
+ u32 diameter_y;
+ u32 rotation_angle;
+};
+} // namespace Settings
diff --git a/src/input_common/touch_from_button.cpp b/src/input_common/touch_from_button.cpp
new file mode 100644
index 000000000..c37716aae
--- /dev/null
+++ b/src/input_common/touch_from_button.cpp
@@ -0,0 +1,52 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "core/frontend/framebuffer_layout.h"
+#include "core/settings.h"
+#include "input_common/touch_from_button.h"
+
+namespace InputCommon {
+
+class TouchFromButtonDevice final : public Input::TouchDevice {
+public:
+ TouchFromButtonDevice() {
+ const auto button_index =
+ static_cast<std::size_t>(Settings::values.touch_from_button_map_index);
+ const auto& buttons = Settings::values.touch_from_button_maps[button_index].buttons;
+
+ for (const auto& config_entry : buttons) {
+ const Common::ParamPackage package{config_entry};
+ map.emplace_back(
+ Input::CreateDevice<Input::ButtonDevice>(config_entry),
+ std::clamp(package.Get("x", 0), 0, static_cast<int>(Layout::ScreenUndocked::Width)),
+ std::clamp(package.Get("y", 0), 0,
+ static_cast<int>(Layout::ScreenUndocked::Height)));
+ }
+ }
+
+ std::tuple<float, float, bool> GetStatus() const override {
+ for (const auto& m : map) {
+ const bool state = std::get<0>(m)->GetStatus();
+ if (state) {
+ const float x = static_cast<float>(std::get<1>(m)) /
+ static_cast<int>(Layout::ScreenUndocked::Width);
+ const float y = static_cast<float>(std::get<2>(m)) /
+ static_cast<int>(Layout::ScreenUndocked::Height);
+ return {x, y, true};
+ }
+ }
+ return {};
+ }
+
+private:
+ // A vector of the mapped button, its x and its y-coordinate
+ std::vector<std::tuple<std::unique_ptr<Input::ButtonDevice>, int, int>> map;
+};
+
+std::unique_ptr<Input::TouchDevice> TouchFromButtonFactory::Create(
+ const Common::ParamPackage& params) {
+ return std::make_unique<TouchFromButtonDevice>();
+}
+
+} // namespace InputCommon
diff --git a/src/input_common/touch_from_button.h b/src/input_common/touch_from_button.h
new file mode 100644
index 000000000..8b4d1aa96
--- /dev/null
+++ b/src/input_common/touch_from_button.h
@@ -0,0 +1,23 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "core/frontend/input.h"
+
+namespace InputCommon {
+
+/**
+ * A touch device factory that takes a list of button devices and combines them into a touch device.
+ */
+class TouchFromButtonFactory final : public Input::Factory<Input::TouchDevice> {
+public:
+ /**
+ * Creates a touch device from a list of button devices
+ */
+ std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
+};
+
+} // namespace InputCommon
diff --git a/src/input_common/udp/client.cpp b/src/input_common/udp/client.cpp
index 3f4eaf448..3677e79ca 100644
--- a/src/input_common/udp/client.cpp
+++ b/src/input_common/udp/client.cpp
@@ -2,14 +2,13 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
-#include <algorithm>
-#include <array>
#include <chrono>
#include <cstring>
#include <functional>
#include <thread>
#include <boost/asio.hpp>
#include "common/logging/log.h"
+#include "core/settings.h"
#include "input_common/udp/client.h"
#include "input_common/udp/protocol.h"
@@ -27,11 +26,11 @@ class Socket {
public:
using clock = std::chrono::system_clock;
- explicit Socket(const std::string& host, u16 port, u8 pad_index, u32 client_id,
- SocketCallback callback)
- : callback(std::move(callback)), timer(io_service),
- socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id),
- pad_index(pad_index) {
+ explicit Socket(const std::string& host, u16 port, std::size_t pad_index_, u32 client_id_,
+ SocketCallback callback_)
+ : callback(std::move(callback_)), timer(io_service),
+ socket(io_service, udp::endpoint(udp::v4(), 0)), client_id(client_id_),
+ pad_index(pad_index_) {
boost::system::error_code ec{};
auto ipv4 = boost::asio::ip::make_address_v4(host, ec);
if (ec.value() != boost::system::errc::success) {
@@ -94,13 +93,17 @@ private:
void HandleSend(const boost::system::error_code& error) {
boost::system::error_code _ignored{};
// Send a request for getting port info for the pad
- Request::PortInfo port_info{1, {pad_index, 0, 0, 0}};
+ const Request::PortInfo port_info{1, {static_cast<u8>(pad_index), 0, 0, 0}};
const auto port_message = Request::Create(port_info, client_id);
std::memcpy(&send_buffer1, &port_message, PORT_INFO_SIZE);
socket.send_to(boost::asio::buffer(send_buffer1), send_endpoint, {}, _ignored);
// Send a request for getting pad data for the pad
- Request::PadData pad_data{Request::PadData::Flags::Id, pad_index, EMPTY_MAC_ADDRESS};
+ const Request::PadData pad_data{
+ Request::PadData::Flags::Id,
+ static_cast<u8>(pad_index),
+ EMPTY_MAC_ADDRESS,
+ };
const auto pad_message = Request::Create(pad_data, client_id);
std::memcpy(send_buffer2.data(), &pad_message, PAD_DATA_SIZE);
socket.send_to(boost::asio::buffer(send_buffer2), send_endpoint, {}, _ignored);
@@ -113,7 +116,7 @@ private:
udp::socket socket;
u32 client_id{};
- u8 pad_index{};
+ std::size_t pad_index{};
static constexpr std::size_t PORT_INFO_SIZE = sizeof(Message<Request::PortInfo>);
static constexpr std::size_t PAD_DATA_SIZE = sizeof(Message<Request::PadData>);
@@ -131,21 +134,59 @@ static void SocketLoop(Socket* socket) {
socket->Loop();
}
-Client::Client(std::shared_ptr<DeviceStatus> status, const std::string& host, u16 port,
- u8 pad_index, u32 client_id)
- : status(std::move(status)) {
- StartCommunication(host, port, pad_index, client_id);
+Client::Client() {
+ LOG_INFO(Input, "Udp Initialization started");
+ for (std::size_t client = 0; client < clients.size(); client++) {
+ const auto pad = client % 4;
+ StartCommunication(client, Settings::values.udp_input_address,
+ Settings::values.udp_input_port, pad, 24872);
+ // Set motion parameters
+ // SetGyroThreshold value should be dependent on GyroscopeZeroDriftMode
+ // Real HW values are unknown, 0.0001 is an approximate to Standard
+ clients[client].motion.SetGyroThreshold(0.0001f);
+ }
}
Client::~Client() {
- socket->Stop();
- thread.join();
+ Reset();
+}
+
+std::vector<Common::ParamPackage> Client::GetInputDevices() const {
+ std::vector<Common::ParamPackage> devices;
+ for (std::size_t client = 0; client < clients.size(); client++) {
+ if (!DeviceConnected(client)) {
+ continue;
+ }
+ std::string name = fmt::format("UDP Controller {}", client);
+ devices.emplace_back(Common::ParamPackage{
+ {"class", "cemuhookudp"},
+ {"display", std::move(name)},
+ {"port", std::to_string(client)},
+ });
+ }
+ return devices;
}
-void Client::ReloadSocket(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
- socket->Stop();
- thread.join();
- StartCommunication(host, port, pad_index, client_id);
+bool Client::DeviceConnected(std::size_t pad) const {
+ // Use last timestamp to detect if the socket has stopped sending data
+ const auto now = std::chrono::system_clock::now();
+ const auto time_difference = static_cast<u64>(
+ std::chrono::duration_cast<std::chrono::milliseconds>(now - clients[pad].last_motion_update)
+ .count());
+ return time_difference < 1000 && clients[pad].active == 1;
+}
+
+void Client::ReloadUDPClient() {
+ for (std::size_t client = 0; client < clients.size(); client++) {
+ ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port, client);
+ }
+}
+void Client::ReloadSocket(const std::string& host, u16 port, std::size_t pad_index, u32 client_id) {
+ // client number must be determined from host / port and pad index
+ const std::size_t client = pad_index;
+ clients[client].socket->Stop();
+ clients[client].thread.join();
+ StartCommunication(client, host, port, pad_index, client_id);
}
void Client::OnVersion(Response::Version data) {
@@ -157,23 +198,36 @@ void Client::OnPortInfo(Response::PortInfo data) {
}
void Client::OnPadData(Response::PadData data) {
+ // Client number must be determined from host / port and pad index
+ const std::size_t client = data.info.id;
LOG_TRACE(Input, "PadData packet received");
- if (data.packet_counter <= packet_sequence) {
+ if (data.packet_counter == clients[client].packet_sequence) {
LOG_WARNING(
Input,
"PadData packet dropped because its stale info. Current count: {} Packet count: {}",
- packet_sequence, data.packet_counter);
+ clients[client].packet_sequence, data.packet_counter);
return;
}
- packet_sequence = data.packet_counter;
- // TODO: Check how the Switch handles motions and how the CemuhookUDP motion
- // directions correspond to the ones of the Switch
- Common::Vec3f accel = Common::MakeVec<float>(data.accel.x, data.accel.y, data.accel.z);
- Common::Vec3f gyro = Common::MakeVec<float>(data.gyro.pitch, data.gyro.yaw, data.gyro.roll);
- {
- std::lock_guard guard(status->update_mutex);
+ clients[client].active = data.info.is_pad_active;
+ clients[client].packet_sequence = data.packet_counter;
+ const auto now = std::chrono::system_clock::now();
+ const auto time_difference =
+ static_cast<u64>(std::chrono::duration_cast<std::chrono::microseconds>(
+ now - clients[client].last_motion_update)
+ .count());
+ clients[client].last_motion_update = now;
+ const Common::Vec3f raw_gyroscope = {data.gyro.pitch, data.gyro.roll, -data.gyro.yaw};
+ clients[client].motion.SetAcceleration({data.accel.x, -data.accel.z, data.accel.y});
+ // Gyroscope values are not it the correct scale from better joy.
+ // Dividing by 312 allows us to make one full turn = 1 turn
+ // This must be a configurable valued called sensitivity
+ clients[client].motion.SetGyroscope(raw_gyroscope / 312.0f);
+ clients[client].motion.UpdateRotation(time_difference);
+ clients[client].motion.UpdateOrientation(time_difference);
- status->motion_status = {accel, gyro};
+ {
+ std::lock_guard guard(clients[client].status.update_mutex);
+ clients[client].status.motion_status = clients[client].motion.GetMotion();
// TODO: add a setting for "click" touch. Click touch refers to a device that differentiates
// between a simple "tap" and a hard press that causes the touch screen to click.
@@ -182,41 +236,115 @@ void Client::OnPadData(Response::PadData data) {
float x = 0;
float y = 0;
- if (is_active && status->touch_calibration) {
- const u16 min_x = status->touch_calibration->min_x;
- const u16 max_x = status->touch_calibration->max_x;
- const u16 min_y = status->touch_calibration->min_y;
- const u16 max_y = status->touch_calibration->max_y;
+ if (is_active && clients[client].status.touch_calibration) {
+ const u16 min_x = clients[client].status.touch_calibration->min_x;
+ const u16 max_x = clients[client].status.touch_calibration->max_x;
+ const u16 min_y = clients[client].status.touch_calibration->min_y;
+ const u16 max_y = clients[client].status.touch_calibration->max_y;
- x = (std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) - min_x) /
+ x = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.x), min_x, max_x) -
+ min_x) /
static_cast<float>(max_x - min_x);
- y = (std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) - min_y) /
+ y = static_cast<float>(std::clamp(static_cast<u16>(data.touch_1.y), min_y, max_y) -
+ min_y) /
static_cast<float>(max_y - min_y);
}
- status->touch_status = {x, y, is_active};
+ clients[client].status.touch_status = {x, y, is_active};
+
+ if (configuring) {
+ const Common::Vec3f gyroscope = clients[client].motion.GetGyroscope();
+ const Common::Vec3f accelerometer = clients[client].motion.GetAcceleration();
+ UpdateYuzuSettings(client, accelerometer, gyroscope, is_active);
+ }
}
}
-void Client::StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id) {
+void Client::StartCommunication(std::size_t client, const std::string& host, u16 port,
+ std::size_t pad_index, u32 client_id) {
SocketCallback callback{[this](Response::Version version) { OnVersion(version); },
[this](Response::PortInfo info) { OnPortInfo(info); },
[this](Response::PadData data) { OnPadData(data); }};
LOG_INFO(Input, "Starting communication with UDP input server on {}:{}", host, port);
- socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
- thread = std::thread{SocketLoop, this->socket.get()};
+ clients[client].socket = std::make_unique<Socket>(host, port, pad_index, client_id, callback);
+ clients[client].thread = std::thread{SocketLoop, clients[client].socket.get()};
+}
+
+void Client::Reset() {
+ for (auto& client : clients) {
+ client.socket->Stop();
+ client.thread.join();
+ }
+}
+
+void Client::UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
+ const Common::Vec3<float>& gyro, bool touch) {
+ if (gyro.Length() > 0.2f) {
+ LOG_DEBUG(Input, "UDP Controller {}: gyro=({}, {}, {}), accel=({}, {}, {}), touch={}",
+ client, gyro[0], gyro[1], gyro[2], acc[0], acc[1], acc[2], touch);
+ }
+ UDPPadStatus pad;
+ if (touch) {
+ pad.touch = PadTouch::Click;
+ pad_queue[client].Push(pad);
+ }
+ for (size_t i = 0; i < 3; ++i) {
+ if (gyro[i] > 5.0f || gyro[i] < -5.0f) {
+ pad.motion = static_cast<PadMotion>(i);
+ pad.motion_value = gyro[i];
+ pad_queue[client].Push(pad);
+ }
+ if (acc[i] > 1.75f || acc[i] < -1.75f) {
+ pad.motion = static_cast<PadMotion>(i + 3);
+ pad.motion_value = acc[i];
+ pad_queue[client].Push(pad);
+ }
+ }
+}
+
+void Client::BeginConfiguration() {
+ for (auto& pq : pad_queue) {
+ pq.Clear();
+ }
+ configuring = true;
+}
+
+void Client::EndConfiguration() {
+ for (auto& pq : pad_queue) {
+ pq.Clear();
+ }
+ configuring = false;
}
-void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id,
- std::function<void()> success_callback,
- std::function<void()> failure_callback) {
+DeviceStatus& Client::GetPadState(std::size_t pad) {
+ return clients[pad].status;
+}
+
+const DeviceStatus& Client::GetPadState(std::size_t pad) const {
+ return clients[pad].status;
+}
+
+std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() {
+ return pad_queue;
+}
+
+const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& Client::GetPadQueue() const {
+ return pad_queue;
+}
+
+void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
+ const std::function<void()>& success_callback,
+ const std::function<void()>& failure_callback) {
std::thread([=] {
Common::Event success_event;
- SocketCallback callback{[](Response::Version version) {}, [](Response::PortInfo info) {},
- [&](Response::PadData data) { success_event.Set(); }};
+ SocketCallback callback{
+ .version = [](Response::Version) {},
+ .port_info = [](Response::PortInfo) {},
+ .pad_data = [&](Response::PadData) { success_event.Set(); },
+ };
Socket socket{host, port, pad_index, client_id, std::move(callback)};
std::thread worker_thread{SocketLoop, &socket};
- bool result = success_event.WaitFor(std::chrono::seconds(8));
+ const bool result = success_event.WaitFor(std::chrono::seconds(5));
socket.Stop();
worker_thread.join();
if (result) {
@@ -228,7 +356,7 @@ void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 clie
}
CalibrationConfigurationJob::CalibrationConfigurationJob(
- const std::string& host, u16 port, u8 pad_index, u32 client_id,
+ const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback) {
@@ -248,7 +376,7 @@ CalibrationConfigurationJob::CalibrationConfigurationJob(
current_status = Status::Ready;
status_callback(current_status);
}
- if (!data.touch_1.is_active) {
+ if (data.touch_1.is_active == 0) {
return;
}
LOG_DEBUG(Input, "Current touch: {} {}", data.touch_1.x,
diff --git a/src/input_common/udp/client.h b/src/input_common/udp/client.h
index b8c654755..747e0c0a2 100644
--- a/src/input_common/udp/client.h
+++ b/src/input_common/udp/client.h
@@ -12,8 +12,12 @@
#include <thread>
#include <tuple>
#include "common/common_types.h"
+#include "common/param_package.h"
#include "common/thread.h"
+#include "common/threadsafe_queue.h"
#include "common/vector_math.h"
+#include "core/frontend/input.h"
+#include "input_common/motion_input.h"
namespace InputCommon::CemuhookUDP {
@@ -28,9 +32,30 @@ struct PortInfo;
struct Version;
} // namespace Response
+enum class PadMotion {
+ GyroX,
+ GyroY,
+ GyroZ,
+ AccX,
+ AccY,
+ AccZ,
+ Undefined,
+};
+
+enum class PadTouch {
+ Click,
+ Undefined,
+};
+
+struct UDPPadStatus {
+ PadTouch touch{PadTouch::Undefined};
+ PadMotion motion{PadMotion::Undefined};
+ f32 motion_value{0.0f};
+};
+
struct DeviceStatus {
std::mutex update_mutex;
- std::tuple<Common::Vec3<float>, Common::Vec3<float>> motion_status;
+ Input::MotionStatus motion_status;
std::tuple<float, float, bool> touch_status;
// calibration data for scaling the device's touch area to 3ds
@@ -45,22 +70,58 @@ struct DeviceStatus {
class Client {
public:
- explicit Client(std::shared_ptr<DeviceStatus> status, const std::string& host = DEFAULT_ADDR,
- u16 port = DEFAULT_PORT, u8 pad_index = 0, u32 client_id = 24872);
+ // Initialize the UDP client capture and read sequence
+ Client();
+
+ // Close and release the client
~Client();
- void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760, u8 pad_index = 0,
- u32 client_id = 24872);
+
+ // Used for polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ std::vector<Common::ParamPackage> GetInputDevices() const;
+
+ bool DeviceConnected(std::size_t pad) const;
+ void ReloadUDPClient();
+ void ReloadSocket(const std::string& host = "127.0.0.1", u16 port = 26760,
+ std::size_t pad_index = 0, u32 client_id = 24872);
+
+ std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue();
+ const std::array<Common::SPSCQueue<UDPPadStatus>, 4>& GetPadQueue() const;
+
+ DeviceStatus& GetPadState(std::size_t pad);
+ const DeviceStatus& GetPadState(std::size_t pad) const;
private:
+ struct ClientData {
+ std::unique_ptr<Socket> socket;
+ DeviceStatus status;
+ std::thread thread;
+ u64 packet_sequence = 0;
+ u8 active = 0;
+
+ // Realtime values
+ // motion is initalized with PID values for drift correction on joycons
+ InputCommon::MotionInput motion{0.3f, 0.005f, 0.0f};
+ std::chrono::time_point<std::chrono::system_clock> last_motion_update;
+ };
+
+ // For shutting down, clear all data, join all threads, release usb
+ void Reset();
+
void OnVersion(Response::Version);
void OnPortInfo(Response::PortInfo);
void OnPadData(Response::PadData);
- void StartCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id);
+ void StartCommunication(std::size_t client, const std::string& host, u16 port,
+ std::size_t pad_index, u32 client_id);
+ void UpdateYuzuSettings(std::size_t client, const Common::Vec3<float>& acc,
+ const Common::Vec3<float>& gyro, bool touch);
+
+ bool configuring = false;
- std::unique_ptr<Socket> socket;
- std::shared_ptr<DeviceStatus> status;
- std::thread thread;
- u64 packet_sequence = 0;
+ std::array<ClientData, 4> clients;
+ std::array<Common::SPSCQueue<UDPPadStatus>, 4> pad_queue;
};
/// An async job allowing configuration of the touchpad calibration.
@@ -78,7 +139,7 @@ public:
* @param status_callback Callback for job status updates
* @param data_callback Called when calibration data is ready
*/
- explicit CalibrationConfigurationJob(const std::string& host, u16 port, u8 pad_index,
+ explicit CalibrationConfigurationJob(const std::string& host, u16 port, std::size_t pad_index,
u32 client_id, std::function<void(Status)> status_callback,
std::function<void(u16, u16, u16, u16)> data_callback);
~CalibrationConfigurationJob();
@@ -88,8 +149,8 @@ private:
Common::Event complete_event;
};
-void TestCommunication(const std::string& host, u16 port, u8 pad_index, u32 client_id,
- std::function<void()> success_callback,
- std::function<void()> failure_callback);
+void TestCommunication(const std::string& host, u16 port, std::size_t pad_index, u32 client_id,
+ const std::function<void()>& success_callback,
+ const std::function<void()>& failure_callback);
} // namespace InputCommon::CemuhookUDP
diff --git a/src/input_common/udp/udp.cpp b/src/input_common/udp/udp.cpp
index 8c6ef1394..71a76a7aa 100644
--- a/src/input_common/udp/udp.cpp
+++ b/src/input_common/udp/udp.cpp
@@ -1,99 +1,142 @@
-// Copyright 2018 Citra Emulator Project
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#include <mutex>
-#include <optional>
-#include <tuple>
-
-#include "common/param_package.h"
-#include "core/frontend/input.h"
-#include "core/settings.h"
+#include <utility>
+#include "common/assert.h"
+#include "common/threadsafe_queue.h"
#include "input_common/udp/client.h"
#include "input_common/udp/udp.h"
-namespace InputCommon::CemuhookUDP {
+namespace InputCommon {
-class UDPTouchDevice final : public Input::TouchDevice {
+class UDPMotion final : public Input::MotionDevice {
public:
- explicit UDPTouchDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
- std::tuple<float, float, bool> GetStatus() const override {
- std::lock_guard guard(status->update_mutex);
- return status->touch_status;
+ explicit UDPMotion(std::string ip_, int port_, u32 pad_, CemuhookUDP::Client* client_)
+ : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
+
+ Input::MotionStatus GetStatus() const override {
+ return client->GetPadState(pad).motion_status;
}
private:
- std::shared_ptr<DeviceStatus> status;
+ const std::string ip;
+ const int port;
+ const u32 pad;
+ CemuhookUDP::Client* client;
+ mutable std::mutex mutex;
};
-class UDPMotionDevice final : public Input::MotionDevice {
-public:
- explicit UDPMotionDevice(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
- std::tuple<Common::Vec3<float>, Common::Vec3<float>> GetStatus() const override {
- std::lock_guard guard(status->update_mutex);
- return status->motion_status;
- }
+/// A motion device factory that creates motion devices from JC Adapter
+UDPMotionFactory::UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_)
+ : client(std::move(client_)) {}
+
+/**
+ * Creates motion device
+ * @param params contains parameters for creating the device:
+ * - "port": the nth jcpad on the adapter
+ */
+std::unique_ptr<Input::MotionDevice> UDPMotionFactory::Create(const Common::ParamPackage& params) {
+ auto ip = params.Get("ip", "127.0.0.1");
+ const auto port = params.Get("port", 26760);
+ const auto pad = static_cast<u32>(params.Get("pad_index", 0));
+
+ return std::make_unique<UDPMotion>(std::move(ip), port, pad, client.get());
+}
-private:
- std::shared_ptr<DeviceStatus> status;
-};
+void UDPMotionFactory::BeginConfiguration() {
+ polling = true;
+ client->BeginConfiguration();
+}
-class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
-public:
- explicit UDPTouchFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
-
- std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override {
- {
- std::lock_guard guard(status->update_mutex);
- status->touch_calibration = DeviceStatus::CalibrationData{};
- // These default values work well for DS4 but probably not other touch inputs
- status->touch_calibration->min_x = params.Get("min_x", 100);
- status->touch_calibration->min_y = params.Get("min_y", 50);
- status->touch_calibration->max_x = params.Get("max_x", 1800);
- status->touch_calibration->max_y = params.Get("max_y", 850);
+void UDPMotionFactory::EndConfiguration() {
+ polling = false;
+ client->EndConfiguration();
+}
+
+Common::ParamPackage UDPMotionFactory::GetNextInput() {
+ Common::ParamPackage params;
+ CemuhookUDP::UDPPadStatus pad;
+ auto& queue = client->GetPadQueue();
+ for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
+ while (queue[pad_number].Pop(pad)) {
+ if (pad.motion == CemuhookUDP::PadMotion::Undefined || std::abs(pad.motion_value) < 1) {
+ continue;
+ }
+ params.Set("engine", "cemuhookudp");
+ params.Set("ip", "127.0.0.1");
+ params.Set("port", 26760);
+ params.Set("pad_index", static_cast<int>(pad_number));
+ params.Set("motion", static_cast<u16>(pad.motion));
+ return params;
}
- return std::make_unique<UDPTouchDevice>(status);
}
+ return params;
+}
-private:
- std::shared_ptr<DeviceStatus> status;
-};
-
-class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
+class UDPTouch final : public Input::TouchDevice {
public:
- explicit UDPMotionFactory(std::shared_ptr<DeviceStatus> status_) : status(std::move(status_)) {}
+ explicit UDPTouch(std::string ip_, int port_, u32 pad_, CemuhookUDP::Client* client_)
+ : ip(std::move(ip_)), port(port_), pad(pad_), client(client_) {}
- std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override {
- return std::make_unique<UDPMotionDevice>(status);
+ std::tuple<float, float, bool> GetStatus() const override {
+ return client->GetPadState(pad).touch_status;
}
private:
- std::shared_ptr<DeviceStatus> status;
+ const std::string ip;
+ const int port;
+ const u32 pad;
+ CemuhookUDP::Client* client;
+ mutable std::mutex mutex;
};
-State::State() {
- auto status = std::make_shared<DeviceStatus>();
- client =
- std::make_unique<Client>(status, Settings::values.udp_input_address,
- Settings::values.udp_input_port, Settings::values.udp_pad_index);
-
- Input::RegisterFactory<Input::TouchDevice>("cemuhookudp",
- std::make_shared<UDPTouchFactory>(status));
- Input::RegisterFactory<Input::MotionDevice>("cemuhookudp",
- std::make_shared<UDPMotionFactory>(status));
+/// A motion device factory that creates motion devices from JC Adapter
+UDPTouchFactory::UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_)
+ : client(std::move(client_)) {}
+
+/**
+ * Creates motion device
+ * @param params contains parameters for creating the device:
+ * - "port": the nth jcpad on the adapter
+ */
+std::unique_ptr<Input::TouchDevice> UDPTouchFactory::Create(const Common::ParamPackage& params) {
+ auto ip = params.Get("ip", "127.0.0.1");
+ const auto port = params.Get("port", 26760);
+ const auto pad = static_cast<u32>(params.Get("pad_index", 0));
+
+ return std::make_unique<UDPTouch>(std::move(ip), port, pad, client.get());
}
-State::~State() {
- Input::UnregisterFactory<Input::TouchDevice>("cemuhookudp");
- Input::UnregisterFactory<Input::MotionDevice>("cemuhookudp");
+void UDPTouchFactory::BeginConfiguration() {
+ polling = true;
+ client->BeginConfiguration();
}
-void State::ReloadUDPClient() {
- client->ReloadSocket(Settings::values.udp_input_address, Settings::values.udp_input_port,
- Settings::values.udp_pad_index);
+void UDPTouchFactory::EndConfiguration() {
+ polling = false;
+ client->EndConfiguration();
}
-std::unique_ptr<State> Init() {
- return std::make_unique<State>();
+Common::ParamPackage UDPTouchFactory::GetNextInput() {
+ Common::ParamPackage params;
+ CemuhookUDP::UDPPadStatus pad;
+ auto& queue = client->GetPadQueue();
+ for (std::size_t pad_number = 0; pad_number < queue.size(); ++pad_number) {
+ while (queue[pad_number].Pop(pad)) {
+ if (pad.touch == CemuhookUDP::PadTouch::Undefined) {
+ continue;
+ }
+ params.Set("engine", "cemuhookudp");
+ params.Set("ip", "127.0.0.1");
+ params.Set("port", 26760);
+ params.Set("pad_index", static_cast<int>(pad_number));
+ params.Set("touch", static_cast<u16>(pad.touch));
+ return params;
+ }
+ }
+ return params;
}
-} // namespace InputCommon::CemuhookUDP
+
+} // namespace InputCommon
diff --git a/src/input_common/udp/udp.h b/src/input_common/udp/udp.h
index 4f83f0441..ea3fd4175 100644
--- a/src/input_common/udp/udp.h
+++ b/src/input_common/udp/udp.h
@@ -1,25 +1,57 @@
-// Copyright 2018 Citra Emulator Project
+// Copyright 2020 yuzu Emulator Project
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
#pragma once
#include <memory>
+#include "core/frontend/input.h"
+#include "input_common/udp/client.h"
-namespace InputCommon::CemuhookUDP {
+namespace InputCommon {
-class Client;
-
-class State {
+/// A motion device factory that creates motion devices from udp clients
+class UDPMotionFactory final : public Input::Factory<Input::MotionDevice> {
public:
- State();
- ~State();
- void ReloadUDPClient();
+ explicit UDPMotionFactory(std::shared_ptr<CemuhookUDP::Client> client_);
+
+ std::unique_ptr<Input::MotionDevice> Create(const Common::ParamPackage& params) override;
+
+ Common::ParamPackage GetNextInput();
+
+ /// For device input configuration/polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ bool IsPolling() const {
+ return polling;
+ }
private:
- std::unique_ptr<Client> client;
+ std::shared_ptr<CemuhookUDP::Client> client;
+ bool polling = false;
};
-std::unique_ptr<State> Init();
+/// A touch device factory that creates touch devices from udp clients
+class UDPTouchFactory final : public Input::Factory<Input::TouchDevice> {
+public:
+ explicit UDPTouchFactory(std::shared_ptr<CemuhookUDP::Client> client_);
+
+ std::unique_ptr<Input::TouchDevice> Create(const Common::ParamPackage& params) override;
+
+ Common::ParamPackage GetNextInput();
+
+ /// For device input configuration/polling
+ void BeginConfiguration();
+ void EndConfiguration();
+
+ bool IsPolling() const {
+ return polling;
+ }
+
+private:
+ std::shared_ptr<CemuhookUDP::Client> client;
+ bool polling = false;
+};
-} // namespace InputCommon::CemuhookUDP
+} // namespace InputCommon
diff --git a/src/tests/common/fibers.cpp b/src/tests/common/fibers.cpp
index 4fd92428f..4757dd2b4 100644
--- a/src/tests/common/fibers.cpp
+++ b/src/tests/common/fibers.cpp
@@ -6,18 +6,40 @@
#include <cstdlib>
#include <functional>
#include <memory>
+#include <mutex>
+#include <stdexcept>
#include <thread>
#include <unordered_map>
#include <vector>
#include <catch2/catch.hpp>
-#include <math.h>
+
#include "common/common_types.h"
#include "common/fiber.h"
-#include "common/spin_lock.h"
namespace Common {
+class ThreadIds {
+public:
+ void Register(u32 id) {
+ const auto thread_id = std::this_thread::get_id();
+ std::scoped_lock lock{mutex};
+ if (ids.contains(thread_id)) {
+ throw std::logic_error{"Registering the same thread twice"};
+ }
+ ids.emplace(thread_id, id);
+ }
+
+ [[nodiscard]] u32 Get() const {
+ std::scoped_lock lock{mutex};
+ return ids.at(std::this_thread::get_id());
+ }
+
+private:
+ mutable std::mutex mutex;
+ std::unordered_map<std::thread::id, u32> ids;
+};
+
class TestControl1 {
public:
TestControl1() = default;
@@ -26,7 +48,7 @@ public:
void ExecuteThread(u32 id);
- std::unordered_map<std::thread::id, u32> ids;
+ ThreadIds thread_ids;
std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
std::vector<std::shared_ptr<Common::Fiber>> work_fibers;
std::vector<u32> items;
@@ -39,8 +61,7 @@ static void WorkControl1(void* control) {
}
void TestControl1::DoWork() {
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
u32 value = items[id];
for (u32 i = 0; i < id; i++) {
value++;
@@ -50,8 +71,7 @@ void TestControl1::DoWork() {
}
void TestControl1::ExecuteThread(u32 id) {
- std::thread::id this_id = std::this_thread::get_id();
- ids[this_id] = id;
+ thread_ids.Register(id);
auto thread_fiber = Fiber::ThreadToFiber();
thread_fibers[id] = thread_fiber;
work_fibers[id] = std::make_shared<Fiber>(std::function<void(void*)>{WorkControl1}, this);
@@ -98,8 +118,7 @@ public:
value1 += i;
}
Fiber::YieldTo(fiber1, fiber3);
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
assert1 = id == 1;
value2 += 5000;
Fiber::YieldTo(fiber1, thread_fibers[id]);
@@ -115,8 +134,7 @@ public:
}
void DoWork3() {
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
assert2 = id == 0;
value1 += 1000;
Fiber::YieldTo(fiber3, thread_fibers[id]);
@@ -125,14 +143,12 @@ public:
void ExecuteThread(u32 id);
void CallFiber1() {
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], fiber1);
}
void CallFiber2() {
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], fiber2);
}
@@ -145,7 +161,7 @@ public:
u32 value2{};
std::atomic<bool> trap{true};
std::atomic<bool> trap2{true};
- std::unordered_map<std::thread::id, u32> ids;
+ ThreadIds thread_ids;
std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
std::shared_ptr<Common::Fiber> fiber1;
std::shared_ptr<Common::Fiber> fiber2;
@@ -168,15 +184,13 @@ static void WorkControl2_3(void* control) {
}
void TestControl2::ExecuteThread(u32 id) {
- std::thread::id this_id = std::this_thread::get_id();
- ids[this_id] = id;
+ thread_ids.Register(id);
auto thread_fiber = Fiber::ThreadToFiber();
thread_fibers[id] = thread_fiber;
}
void TestControl2::Exit() {
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
thread_fibers[id]->Exit();
}
@@ -228,24 +242,21 @@ public:
void DoWork1() {
value1 += 1;
Fiber::YieldTo(fiber1, fiber2);
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
value3 += 1;
Fiber::YieldTo(fiber1, thread_fibers[id]);
}
void DoWork2() {
value2 += 1;
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
Fiber::YieldTo(fiber2, thread_fibers[id]);
}
void ExecuteThread(u32 id);
void CallFiber1() {
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
Fiber::YieldTo(thread_fibers[id], fiber1);
}
@@ -254,7 +265,7 @@ public:
u32 value1{};
u32 value2{};
u32 value3{};
- std::unordered_map<std::thread::id, u32> ids;
+ ThreadIds thread_ids;
std::vector<std::shared_ptr<Common::Fiber>> thread_fibers;
std::shared_ptr<Common::Fiber> fiber1;
std::shared_ptr<Common::Fiber> fiber2;
@@ -271,15 +282,13 @@ static void WorkControl3_2(void* control) {
}
void TestControl3::ExecuteThread(u32 id) {
- std::thread::id this_id = std::this_thread::get_id();
- ids[this_id] = id;
+ thread_ids.Register(id);
auto thread_fiber = Fiber::ThreadToFiber();
thread_fibers[id] = thread_fiber;
}
void TestControl3::Exit() {
- std::thread::id this_id = std::this_thread::get_id();
- u32 id = ids[this_id];
+ const u32 id = thread_ids.Get();
thread_fibers[id]->Exit();
}
diff --git a/src/video_core/CMakeLists.txt b/src/video_core/CMakeLists.txt
index 3cd896a0f..abcee2a1c 100644
--- a/src/video_core/CMakeLists.txt
+++ b/src/video_core/CMakeLists.txt
@@ -1,8 +1,28 @@
+add_subdirectory(host_shaders)
+
add_library(video_core STATIC
buffer_cache/buffer_block.h
buffer_cache/buffer_cache.h
buffer_cache/map_interval.cpp
buffer_cache/map_interval.h
+ cdma_pusher.cpp
+ cdma_pusher.h
+ command_classes/codecs/codec.cpp
+ command_classes/codecs/codec.h
+ command_classes/codecs/h264.cpp
+ command_classes/codecs/h264.h
+ command_classes/codecs/vp9.cpp
+ command_classes/codecs/vp9.h
+ command_classes/codecs/vp9_types.h
+ command_classes/host1x.cpp
+ command_classes/host1x.h
+ command_classes/nvdec.cpp
+ command_classes/nvdec.h
+ command_classes/nvdec_common.h
+ command_classes/sync_manager.cpp
+ command_classes/sync_manager.h
+ command_classes/vic.cpp
+ command_classes/vic.h
compatible_formats.cpp
compatible_formats.h
dirty_flags.cpp
@@ -188,6 +208,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_blit_screen.h
renderer_vulkan/vk_buffer_cache.cpp
renderer_vulkan/vk_buffer_cache.h
+ renderer_vulkan/vk_command_pool.cpp
+ renderer_vulkan/vk_command_pool.h
renderer_vulkan/vk_compute_pass.cpp
renderer_vulkan/vk_compute_pass.h
renderer_vulkan/vk_compute_pipeline.cpp
@@ -202,6 +224,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_graphics_pipeline.h
renderer_vulkan/vk_image.cpp
renderer_vulkan/vk_image.h
+ renderer_vulkan/vk_master_semaphore.cpp
+ renderer_vulkan/vk_master_semaphore.h
renderer_vulkan/vk_memory_manager.cpp
renderer_vulkan/vk_memory_manager.h
renderer_vulkan/vk_pipeline_cache.cpp
@@ -212,8 +236,8 @@ if (ENABLE_VULKAN)
renderer_vulkan/vk_rasterizer.h
renderer_vulkan/vk_renderpass_cache.cpp
renderer_vulkan/vk_renderpass_cache.h
- renderer_vulkan/vk_resource_manager.cpp
- renderer_vulkan/vk_resource_manager.h
+ renderer_vulkan/vk_resource_pool.cpp
+ renderer_vulkan/vk_resource_pool.h
renderer_vulkan/vk_sampler_cache.cpp
renderer_vulkan/vk_sampler_cache.h
renderer_vulkan/vk_scheduler.cpp
@@ -244,6 +268,17 @@ create_target_directory_groups(video_core)
target_link_libraries(video_core PUBLIC common core)
target_link_libraries(video_core PRIVATE glad xbyak)
+if (MSVC)
+ target_include_directories(video_core PRIVATE ${FFMPEG_INCLUDE_DIR})
+ target_link_libraries(video_core PUBLIC ${FFMPEG_LIBRARY_DIR}/swscale.lib ${FFMPEG_LIBRARY_DIR}/avcodec.lib ${FFMPEG_LIBRARY_DIR}/avutil.lib)
+else()
+ target_include_directories(video_core PRIVATE ${FFMPEG_INCLUDE_DIR})
+ target_link_libraries(video_core PRIVATE ${FFMPEG_LIBRARIES})
+endif()
+
+add_dependencies(video_core host_shaders)
+target_include_directories(video_core PRIVATE ${HOST_SHADERS_INCLUDE})
+
if (ENABLE_VULKAN)
target_include_directories(video_core PRIVATE sirit ../../externals/Vulkan-Headers/include)
target_compile_definitions(video_core PRIVATE HAS_VULKAN)
@@ -264,5 +299,17 @@ endif()
if (MSVC)
target_compile_options(video_core PRIVATE /we4267)
else()
- target_compile_options(video_core PRIVATE -Werror=conversion -Wno-error=sign-conversion)
+ target_compile_options(video_core PRIVATE
+ -Werror=conversion
+ -Wno-error=sign-conversion
+ -Werror=pessimizing-move
+ -Werror=redundant-move
+ -Werror=switch
+ -Werror=type-limits
+ -Werror=unused-variable
+
+ $<$<CXX_COMPILER_ID:GNU>:-Werror=class-memaccess>
+ $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-parameter>
+ $<$<CXX_COMPILER_ID:GNU>:-Werror=unused-but-set-variable>
+ )
endif()
diff --git a/src/video_core/buffer_cache/buffer_cache.h b/src/video_core/buffer_cache/buffer_cache.h
index b5dc68902..e7edd733f 100644
--- a/src/video_core/buffer_cache/buffer_cache.h
+++ b/src/video_core/buffer_cache/buffer_cache.h
@@ -51,46 +51,43 @@ public:
bool is_written = false, bool use_fast_cbuf = false) {
std::lock_guard lock{mutex};
- auto& memory_manager = system.GPU().MemoryManager();
- const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr);
- if (!cpu_addr_opt) {
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
+ if (!cpu_addr) {
return GetEmptyBuffer(size);
}
- const VAddr cpu_addr = *cpu_addr_opt;
// Cache management is a big overhead, so only cache entries with a given size.
// TODO: Figure out which size is the best for given games.
constexpr std::size_t max_stream_size = 0x800;
if (use_fast_cbuf || size < max_stream_size) {
- if (!is_written && !IsRegionWritten(cpu_addr, cpu_addr + size - 1)) {
- const bool is_granular = memory_manager.IsGranularRange(gpu_addr, size);
+ if (!is_written && !IsRegionWritten(*cpu_addr, *cpu_addr + size - 1)) {
+ const bool is_granular = gpu_memory.IsGranularRange(gpu_addr, size);
if (use_fast_cbuf) {
u8* dest;
if (is_granular) {
- dest = memory_manager.GetPointer(gpu_addr);
+ dest = gpu_memory.GetPointer(gpu_addr);
} else {
staging_buffer.resize(size);
dest = staging_buffer.data();
- memory_manager.ReadBlockUnsafe(gpu_addr, dest, size);
+ gpu_memory.ReadBlockUnsafe(gpu_addr, dest, size);
}
return ConstBufferUpload(dest, size);
}
if (is_granular) {
- u8* const host_ptr = memory_manager.GetPointer(gpu_addr);
+ u8* const host_ptr = gpu_memory.GetPointer(gpu_addr);
return StreamBufferUpload(size, alignment, [host_ptr, size](u8* dest) {
std::memcpy(dest, host_ptr, size);
});
} else {
- return StreamBufferUpload(
- size, alignment, [&memory_manager, gpu_addr, size](u8* dest) {
- memory_manager.ReadBlockUnsafe(gpu_addr, dest, size);
- });
+ return StreamBufferUpload(size, alignment, [this, gpu_addr, size](u8* dest) {
+ gpu_memory.ReadBlockUnsafe(gpu_addr, dest, size);
+ });
}
}
}
- Buffer* const block = GetBlock(cpu_addr, size);
- MapInterval* const map = MapAddress(block, gpu_addr, cpu_addr, size);
+ Buffer* const block = GetBlock(*cpu_addr, size);
+ MapInterval* const map = MapAddress(block, gpu_addr, *cpu_addr, size);
if (!map) {
return GetEmptyBuffer(size);
}
@@ -106,7 +103,7 @@ public:
}
}
- return BufferInfo{block->Handle(), block->Offset(cpu_addr), block->Address()};
+ return BufferInfo{block->Handle(), block->Offset(*cpu_addr), block->Address()};
}
/// Uploads from a host memory. Returns the OpenGL buffer where it's located and its offset.
@@ -262,9 +259,11 @@ public:
virtual BufferInfo GetEmptyBuffer(std::size_t size) = 0;
protected:
- explicit BufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
- std::unique_ptr<StreamBuffer> stream_buffer)
- : rasterizer{rasterizer}, system{system}, stream_buffer{std::move(stream_buffer)} {}
+ explicit BufferCache(VideoCore::RasterizerInterface& rasterizer_,
+ Tegra::MemoryManager& gpu_memory_, Core::Memory::Memory& cpu_memory_,
+ std::unique_ptr<StreamBuffer> stream_buffer_)
+ : rasterizer{rasterizer_}, gpu_memory{gpu_memory_}, cpu_memory{cpu_memory_},
+ stream_buffer{std::move(stream_buffer_)}, stream_buffer_handle{stream_buffer->Handle()} {}
~BufferCache() = default;
@@ -326,14 +325,13 @@ private:
MapInterval* MapAddress(Buffer* block, GPUVAddr gpu_addr, VAddr cpu_addr, std::size_t size) {
const VectorMapInterval overlaps = GetMapsInRange(cpu_addr, size);
if (overlaps.empty()) {
- auto& memory_manager = system.GPU().MemoryManager();
const VAddr cpu_addr_end = cpu_addr + size;
- if (memory_manager.IsGranularRange(gpu_addr, size)) {
- u8* host_ptr = memory_manager.GetPointer(gpu_addr);
+ if (gpu_memory.IsGranularRange(gpu_addr, size)) {
+ u8* const host_ptr = gpu_memory.GetPointer(gpu_addr);
block->Upload(block->Offset(cpu_addr), size, host_ptr);
} else {
staging_buffer.resize(size);
- memory_manager.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
+ gpu_memory.ReadBlockUnsafe(gpu_addr, staging_buffer.data(), size);
block->Upload(block->Offset(cpu_addr), size, staging_buffer.data());
}
return Register(MapInterval(cpu_addr, cpu_addr_end, gpu_addr));
@@ -392,7 +390,7 @@ private:
continue;
}
staging_buffer.resize(size);
- system.Memory().ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size);
+ cpu_memory.ReadBlockUnsafe(interval.lower(), staging_buffer.data(), size);
block->Upload(block->Offset(interval.lower()), size, staging_buffer.data());
}
}
@@ -431,7 +429,7 @@ private:
const std::size_t size = map->end - map->start;
staging_buffer.resize(size);
block->Download(block->Offset(map->start), size, staging_buffer.data());
- system.Memory().WriteBlockUnsafe(map->start, staging_buffer.data(), size);
+ cpu_memory.WriteBlockUnsafe(map->start, staging_buffer.data(), size);
map->MarkAsModified(false, 0);
}
@@ -567,7 +565,8 @@ private:
}
VideoCore::RasterizerInterface& rasterizer;
- Core::System& system;
+ Tegra::MemoryManager& gpu_memory;
+ Core::Memory::Memory& cpu_memory;
std::unique_ptr<StreamBuffer> stream_buffer;
BufferType stream_buffer_handle;
diff --git a/src/video_core/cdma_pusher.cpp b/src/video_core/cdma_pusher.cpp
new file mode 100644
index 000000000..b60f86260
--- /dev/null
+++ b/src/video_core/cdma_pusher.cpp
@@ -0,0 +1,171 @@
+// MIT License
+//
+// Copyright (c) Ryujinx Team and Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include "command_classes/host1x.h"
+#include "command_classes/nvdec.h"
+#include "command_classes/vic.h"
+#include "common/bit_util.h"
+#include "video_core/cdma_pusher.h"
+#include "video_core/command_classes/nvdec_common.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/gpu.h"
+#include "video_core/memory_manager.h"
+
+namespace Tegra {
+CDmaPusher::CDmaPusher(GPU& gpu)
+ : gpu(gpu), nvdec_processor(std::make_shared<Nvdec>(gpu)),
+ vic_processor(std::make_unique<Vic>(gpu, nvdec_processor)),
+ host1x_processor(std::make_unique<Host1x>(gpu)),
+ nvdec_sync(std::make_unique<SyncptIncrManager>(gpu)),
+ vic_sync(std::make_unique<SyncptIncrManager>(gpu)) {}
+
+CDmaPusher::~CDmaPusher() = default;
+
+void CDmaPusher::Push(ChCommandHeaderList&& entries) {
+ cdma_queue.push(std::move(entries));
+}
+
+void CDmaPusher::DispatchCalls() {
+ while (!cdma_queue.empty()) {
+ Step();
+ }
+}
+
+void CDmaPusher::Step() {
+ const auto entries{cdma_queue.front()};
+ cdma_queue.pop();
+
+ std::vector<u32> values(entries.size());
+ std::memcpy(values.data(), entries.data(), entries.size() * sizeof(u32));
+
+ for (const u32 value : values) {
+ if (mask != 0) {
+ const u32 lbs = Common::CountTrailingZeroes32(mask);
+ mask &= ~(1U << lbs);
+ ExecuteCommand(static_cast<u32>(offset + lbs), value);
+ continue;
+ } else if (count != 0) {
+ --count;
+ ExecuteCommand(static_cast<u32>(offset), value);
+ if (incrementing) {
+ ++offset;
+ }
+ continue;
+ }
+ const auto mode = static_cast<ChSubmissionMode>((value >> 28) & 0xf);
+ switch (mode) {
+ case ChSubmissionMode::SetClass: {
+ mask = value & 0x3f;
+ offset = (value >> 16) & 0xfff;
+ current_class = static_cast<ChClassId>((value >> 6) & 0x3ff);
+ break;
+ }
+ case ChSubmissionMode::Incrementing:
+ case ChSubmissionMode::NonIncrementing:
+ count = value & 0xffff;
+ offset = (value >> 16) & 0xfff;
+ incrementing = mode == ChSubmissionMode::Incrementing;
+ break;
+ case ChSubmissionMode::Mask:
+ mask = value & 0xffff;
+ offset = (value >> 16) & 0xfff;
+ break;
+ case ChSubmissionMode::Immediate: {
+ const u32 data = value & 0xfff;
+ offset = (value >> 16) & 0xfff;
+ ExecuteCommand(static_cast<u32>(offset), data);
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("ChSubmission mode {} is not implemented!", static_cast<u32>(mode));
+ break;
+ }
+ }
+}
+
+void CDmaPusher::ExecuteCommand(u32 offset, u32 data) {
+ switch (current_class) {
+ case ChClassId::NvDec:
+ ThiStateWrite(nvdec_thi_state, offset, {data});
+ switch (static_cast<ThiMethod>(offset)) {
+ case ThiMethod::IncSyncpt: {
+ LOG_DEBUG(Service_NVDRV, "NVDEC Class IncSyncpt Method");
+ const auto syncpoint_id = static_cast<u32>(data & 0xFF);
+ const auto cond = static_cast<u32>((data >> 8) & 0xFF);
+ if (cond == 0) {
+ nvdec_sync->Increment(syncpoint_id);
+ } else {
+ nvdec_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id);
+ nvdec_sync->SignalDone(syncpoint_id);
+ }
+ break;
+ }
+ case ThiMethod::SetMethod1:
+ LOG_DEBUG(Service_NVDRV, "NVDEC method 0x{:X}",
+ static_cast<u32>(nvdec_thi_state.method_0));
+ nvdec_processor->ProcessMethod(
+ static_cast<Tegra::Nvdec::Method>(nvdec_thi_state.method_0), {data});
+ break;
+ default:
+ break;
+ }
+ break;
+ case ChClassId::GraphicsVic:
+ ThiStateWrite(vic_thi_state, static_cast<u32>(offset), {data});
+ switch (static_cast<ThiMethod>(offset)) {
+ case ThiMethod::IncSyncpt: {
+ LOG_DEBUG(Service_NVDRV, "VIC Class IncSyncpt Method");
+ const auto syncpoint_id = static_cast<u32>(data & 0xFF);
+ const auto cond = static_cast<u32>((data >> 8) & 0xFF);
+ if (cond == 0) {
+ vic_sync->Increment(syncpoint_id);
+ } else {
+ vic_sync->IncrementWhenDone(static_cast<u32>(current_class), syncpoint_id);
+ vic_sync->SignalDone(syncpoint_id);
+ }
+ break;
+ }
+ case ThiMethod::SetMethod1:
+ LOG_DEBUG(Service_NVDRV, "VIC method 0x{:X}, Args=({})",
+ static_cast<u32>(vic_thi_state.method_0), data);
+ vic_processor->ProcessMethod(static_cast<Tegra::Vic::Method>(vic_thi_state.method_0),
+ {data});
+ break;
+ default:
+ break;
+ }
+ break;
+ case ChClassId::Host1x:
+ // This device is mainly for syncpoint synchronization
+ LOG_DEBUG(Service_NVDRV, "Host1X Class Method");
+ host1x_processor->ProcessMethod(static_cast<Tegra::Host1x::Method>(offset), {data});
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Current class not implemented {:X}", static_cast<u32>(current_class));
+ break;
+ }
+}
+
+void CDmaPusher::ThiStateWrite(ThiRegisters& state, u32 offset, const std::vector<u32>& arguments) {
+ u8* const state_offset = reinterpret_cast<u8*>(&state) + sizeof(u32) * offset;
+ std::memcpy(state_offset, arguments.data(), sizeof(u32) * arguments.size());
+}
+
+} // namespace Tegra
diff --git a/src/video_core/cdma_pusher.h b/src/video_core/cdma_pusher.h
new file mode 100644
index 000000000..982f309c5
--- /dev/null
+++ b/src/video_core/cdma_pusher.h
@@ -0,0 +1,138 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <unordered_map>
+#include <vector>
+#include <queue>
+
+#include "common/bit_field.h"
+#include "common/common_types.h"
+#include "video_core/command_classes/sync_manager.h"
+
+namespace Tegra {
+
+class GPU;
+class Nvdec;
+class Vic;
+class Host1x;
+
+enum class ChSubmissionMode : u32 {
+ SetClass = 0,
+ Incrementing = 1,
+ NonIncrementing = 2,
+ Mask = 3,
+ Immediate = 4,
+ Restart = 5,
+ Gather = 6,
+};
+
+enum class ChClassId : u32 {
+ NoClass = 0x0,
+ Host1x = 0x1,
+ VideoEncodeMpeg = 0x20,
+ VideoEncodeNvEnc = 0x21,
+ VideoStreamingVi = 0x30,
+ VideoStreamingIsp = 0x32,
+ VideoStreamingIspB = 0x34,
+ VideoStreamingViI2c = 0x36,
+ GraphicsVic = 0x5d,
+ Graphics3D = 0x60,
+ GraphicsGpu = 0x61,
+ Tsec = 0xe0,
+ TsecB = 0xe1,
+ NvJpg = 0xc0,
+ NvDec = 0xf0
+};
+
+enum class ChMethod : u32 {
+ Empty = 0,
+ SetMethod = 0x10,
+ SetData = 0x11,
+};
+
+union ChCommandHeader {
+ u32 raw;
+ BitField<0, 16, u32> value;
+ BitField<16, 12, ChMethod> method_offset;
+ BitField<28, 4, ChSubmissionMode> submission_mode;
+};
+static_assert(sizeof(ChCommandHeader) == sizeof(u32), "ChCommand header is an invalid size");
+
+struct ChCommand {
+ ChClassId class_id{};
+ int method_offset{};
+ std::vector<u32> arguments;
+};
+
+using ChCommandHeaderList = std::vector<Tegra::ChCommandHeader>;
+using ChCommandList = std::vector<Tegra::ChCommand>;
+
+struct ThiRegisters {
+ u32_le increment_syncpt{};
+ INSERT_PADDING_WORDS(1);
+ u32_le increment_syncpt_error{};
+ u32_le ctx_switch_incremement_syncpt{};
+ INSERT_PADDING_WORDS(4);
+ u32_le ctx_switch{};
+ INSERT_PADDING_WORDS(1);
+ u32_le ctx_syncpt_eof{};
+ INSERT_PADDING_WORDS(5);
+ u32_le method_0{};
+ u32_le method_1{};
+ INSERT_PADDING_WORDS(12);
+ u32_le int_status{};
+ u32_le int_mask{};
+};
+
+enum class ThiMethod : u32 {
+ IncSyncpt = offsetof(ThiRegisters, increment_syncpt) / sizeof(u32),
+ SetMethod0 = offsetof(ThiRegisters, method_0) / sizeof(u32),
+ SetMethod1 = offsetof(ThiRegisters, method_1) / sizeof(u32),
+};
+
+class CDmaPusher {
+public:
+ explicit CDmaPusher(GPU& gpu);
+ ~CDmaPusher();
+
+ /// Push NVDEC command buffer entries into queue
+ void Push(ChCommandHeaderList&& entries);
+
+ /// Process queued command buffer entries
+ void DispatchCalls();
+
+ /// Process one queue element
+ void Step();
+
+ /// Invoke command class devices to execute the command based on the current state
+ void ExecuteCommand(u32 offset, u32 data);
+
+private:
+ /// Write arguments value to the ThiRegisters member at the specified offset
+ void ThiStateWrite(ThiRegisters& state, u32 offset, const std::vector<u32>& arguments);
+
+ GPU& gpu;
+
+ std::shared_ptr<Tegra::Nvdec> nvdec_processor;
+ std::unique_ptr<Tegra::Vic> vic_processor;
+ std::unique_ptr<Tegra::Host1x> host1x_processor;
+ std::unique_ptr<SyncptIncrManager> nvdec_sync;
+ std::unique_ptr<SyncptIncrManager> vic_sync;
+ ChClassId current_class{};
+ ThiRegisters vic_thi_state{};
+ ThiRegisters nvdec_thi_state{};
+
+ s32 count{};
+ s32 offset{};
+ s32 mask{};
+ bool incrementing{};
+
+ // Queue of command lists to be processed
+ std::queue<ChCommandHeaderList> cdma_queue;
+};
+
+} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/codec.cpp b/src/video_core/command_classes/codecs/codec.cpp
new file mode 100644
index 000000000..1adf3cd13
--- /dev/null
+++ b/src/video_core/command_classes/codecs/codec.cpp
@@ -0,0 +1,115 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring>
+#include <fstream>
+#include <vector>
+#include "common/assert.h"
+#include "video_core/command_classes/codecs/codec.h"
+#include "video_core/command_classes/codecs/h264.h"
+#include "video_core/command_classes/codecs/vp9.h"
+#include "video_core/gpu.h"
+#include "video_core/memory_manager.h"
+
+extern "C" {
+#include <libavutil/opt.h>
+}
+
+namespace Tegra {
+
+Codec::Codec(GPU& gpu_)
+ : gpu(gpu_), h264_decoder(std::make_unique<Decoder::H264>(gpu)),
+ vp9_decoder(std::make_unique<Decoder::VP9>(gpu)) {}
+
+Codec::~Codec() {
+ if (!initialized) {
+ return;
+ }
+ // Free libav memory
+ avcodec_send_packet(av_codec_ctx, nullptr);
+ avcodec_receive_frame(av_codec_ctx, av_frame);
+ avcodec_flush_buffers(av_codec_ctx);
+
+ av_frame_unref(av_frame);
+ av_free(av_frame);
+ avcodec_close(av_codec_ctx);
+}
+
+void Codec::SetTargetCodec(NvdecCommon::VideoCodec codec) {
+ LOG_INFO(Service_NVDRV, "NVDEC video codec initialized to {}", static_cast<u32>(codec));
+ current_codec = codec;
+}
+
+void Codec::StateWrite(u32 offset, u64 arguments) {
+ u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u64);
+ std::memcpy(state_offset, &arguments, sizeof(u64));
+}
+
+void Codec::Decode() {
+ bool is_first_frame = false;
+
+ if (!initialized) {
+ if (current_codec == NvdecCommon::VideoCodec::H264) {
+ av_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
+ } else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
+ av_codec = avcodec_find_decoder(AV_CODEC_ID_VP9);
+ } else {
+ LOG_ERROR(Service_NVDRV, "Unknown video codec {}", static_cast<u32>(current_codec));
+ return;
+ }
+
+ av_codec_ctx = avcodec_alloc_context3(av_codec);
+ av_frame = av_frame_alloc();
+ av_opt_set(av_codec_ctx->priv_data, "tune", "zerolatency", 0);
+
+ // TODO(ameerj): libavcodec gpu hw acceleration
+
+ const auto av_error = avcodec_open2(av_codec_ctx, av_codec, nullptr);
+ if (av_error < 0) {
+ LOG_ERROR(Service_NVDRV, "avcodec_open2() Failed.");
+ av_frame_unref(av_frame);
+ av_free(av_frame);
+ avcodec_close(av_codec_ctx);
+ return;
+ }
+ initialized = true;
+ is_first_frame = true;
+ }
+ bool vp9_hidden_frame = false;
+
+ AVPacket packet{};
+ av_init_packet(&packet);
+ std::vector<u8> frame_data;
+
+ if (current_codec == NvdecCommon::VideoCodec::H264) {
+ frame_data = h264_decoder->ComposeFrameHeader(state, is_first_frame);
+ } else if (current_codec == NvdecCommon::VideoCodec::Vp9) {
+ frame_data = vp9_decoder->ComposeFrameHeader(state);
+ vp9_hidden_frame = vp9_decoder->WasFrameHidden();
+ }
+
+ packet.data = frame_data.data();
+ packet.size = static_cast<int>(frame_data.size());
+
+ avcodec_send_packet(av_codec_ctx, &packet);
+
+ if (!vp9_hidden_frame) {
+ // Only receive/store visible frames
+ avcodec_receive_frame(av_codec_ctx, av_frame);
+ }
+}
+
+AVFrame* Codec::GetCurrentFrame() {
+ return av_frame;
+}
+
+const AVFrame* Codec::GetCurrentFrame() const {
+ return av_frame;
+}
+
+NvdecCommon::VideoCodec Codec::GetCurrentCodec() const {
+ return current_codec;
+}
+
+} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/codec.h b/src/video_core/command_classes/codecs/codec.h
new file mode 100644
index 000000000..5bbe6a332
--- /dev/null
+++ b/src/video_core/command_classes/codecs/codec.h
@@ -0,0 +1,66 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include "common/common_types.h"
+#include "video_core/command_classes/nvdec_common.h"
+
+extern "C" {
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic ignored "-Wconversion"
+#endif
+#include <libavcodec/avcodec.h>
+#if defined(__GNUC__) || defined(__clang__)
+#pragma GCC diagnostic pop
+#endif
+}
+
+namespace Tegra {
+class GPU;
+struct VicRegisters;
+
+namespace Decoder {
+class H264;
+class VP9;
+} // namespace Decoder
+
+class Codec {
+public:
+ explicit Codec(GPU& gpu);
+ ~Codec();
+
+ /// Sets NVDEC video stream codec
+ void SetTargetCodec(NvdecCommon::VideoCodec codec);
+
+ /// Populate NvdecRegisters state with argument value at the provided offset
+ void StateWrite(u32 offset, u64 arguments);
+
+ /// Call decoders to construct headers, decode AVFrame with ffmpeg
+ void Decode();
+
+ /// Returns most recently decoded frame
+ [[nodiscard]] AVFrame* GetCurrentFrame();
+ [[nodiscard]] const AVFrame* GetCurrentFrame() const;
+
+ /// Returns the value of current_codec
+ [[nodiscard]] NvdecCommon::VideoCodec GetCurrentCodec() const;
+
+private:
+ bool initialized{};
+ NvdecCommon::VideoCodec current_codec{NvdecCommon::VideoCodec::None};
+
+ AVCodec* av_codec{nullptr};
+ AVCodecContext* av_codec_ctx{nullptr};
+ AVFrame* av_frame{nullptr};
+
+ GPU& gpu;
+ std::unique_ptr<Decoder::H264> h264_decoder;
+ std::unique_ptr<Decoder::VP9> vp9_decoder;
+
+ NvdecCommon::NvdecRegisters state{};
+};
+
+} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/h264.cpp b/src/video_core/command_classes/codecs/h264.cpp
new file mode 100644
index 000000000..33e063e20
--- /dev/null
+++ b/src/video_core/command_classes/codecs/h264.cpp
@@ -0,0 +1,293 @@
+// MIT License
+//
+// Copyright (c) Ryujinx Team and Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <array>
+#include "common/bit_util.h"
+#include "video_core/command_classes/codecs/h264.h"
+#include "video_core/gpu.h"
+#include "video_core/memory_manager.h"
+
+namespace Tegra::Decoder {
+namespace {
+// ZigZag LUTs from libavcodec.
+constexpr std::array<u8, 64> zig_zag_direct{
+ 0, 1, 8, 16, 9, 2, 3, 10, 17, 24, 32, 25, 18, 11, 4, 5, 12, 19, 26, 33, 40, 48,
+ 41, 34, 27, 20, 13, 6, 7, 14, 21, 28, 35, 42, 49, 56, 57, 50, 43, 36, 29, 22, 15, 23,
+ 30, 37, 44, 51, 58, 59, 52, 45, 38, 31, 39, 46, 53, 60, 61, 54, 47, 55, 62, 63,
+};
+
+constexpr std::array<u8, 16> zig_zag_scan{
+ 0 + 0 * 4, 1 + 0 * 4, 0 + 1 * 4, 0 + 2 * 4, 1 + 1 * 4, 2 + 0 * 4, 3 + 0 * 4, 2 + 1 * 4,
+ 1 + 2 * 4, 0 + 3 * 4, 1 + 3 * 4, 2 + 2 * 4, 3 + 1 * 4, 3 + 2 * 4, 2 + 3 * 4, 3 + 3 * 4,
+};
+} // Anonymous namespace
+
+H264::H264(GPU& gpu_) : gpu(gpu_) {}
+
+H264::~H264() = default;
+
+const std::vector<u8>& H264::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state,
+ bool is_first_frame) {
+ H264DecoderContext context{};
+ gpu.MemoryManager().ReadBlock(state.picture_info_offset, &context, sizeof(H264DecoderContext));
+
+ const s32 frame_number = static_cast<s32>((context.h264_parameter_set.flags >> 46) & 0x1ffff);
+ if (!is_first_frame && frame_number != 0) {
+ frame.resize(context.frame_data_size);
+
+ gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.data(), frame.size());
+ } else {
+ /// Encode header
+ H264BitWriter writer{};
+ writer.WriteU(1, 24);
+ writer.WriteU(0, 1);
+ writer.WriteU(3, 2);
+ writer.WriteU(7, 5);
+ writer.WriteU(100, 8);
+ writer.WriteU(0, 8);
+ writer.WriteU(31, 8);
+ writer.WriteUe(0);
+ const auto chroma_format_idc =
+ static_cast<u32>((context.h264_parameter_set.flags >> 12) & 3);
+ writer.WriteUe(chroma_format_idc);
+ if (chroma_format_idc == 3) {
+ writer.WriteBit(false);
+ }
+
+ writer.WriteUe(0);
+ writer.WriteUe(0);
+ writer.WriteBit(false); // QpprimeYZeroTransformBypassFlag
+ writer.WriteBit(false); // Scaling matrix present flag
+
+ const auto order_cnt_type = static_cast<u32>((context.h264_parameter_set.flags >> 14) & 3);
+ writer.WriteUe(static_cast<u32>((context.h264_parameter_set.flags >> 8) & 0xf));
+ writer.WriteUe(order_cnt_type);
+ if (order_cnt_type == 0) {
+ writer.WriteUe(context.h264_parameter_set.log2_max_pic_order_cnt);
+ } else if (order_cnt_type == 1) {
+ writer.WriteBit(context.h264_parameter_set.delta_pic_order_always_zero_flag != 0);
+
+ writer.WriteSe(0);
+ writer.WriteSe(0);
+ writer.WriteUe(0);
+ }
+
+ const s32 pic_height = context.h264_parameter_set.pic_height_in_map_units /
+ (context.h264_parameter_set.frame_mbs_only_flag ? 1 : 2);
+
+ writer.WriteUe(16);
+ writer.WriteBit(false);
+ writer.WriteUe(context.h264_parameter_set.pic_width_in_mbs - 1);
+ writer.WriteUe(pic_height - 1);
+ writer.WriteBit(context.h264_parameter_set.frame_mbs_only_flag != 0);
+
+ if (!context.h264_parameter_set.frame_mbs_only_flag) {
+ writer.WriteBit(((context.h264_parameter_set.flags >> 0) & 1) != 0);
+ }
+
+ writer.WriteBit(((context.h264_parameter_set.flags >> 1) & 1) != 0);
+ writer.WriteBit(false); // Frame cropping flag
+ writer.WriteBit(false); // VUI parameter present flag
+
+ writer.End();
+
+ // H264 PPS
+ writer.WriteU(1, 24);
+ writer.WriteU(0, 1);
+ writer.WriteU(3, 2);
+ writer.WriteU(8, 5);
+
+ writer.WriteUe(0);
+ writer.WriteUe(0);
+
+ writer.WriteBit(context.h264_parameter_set.entropy_coding_mode_flag != 0);
+ writer.WriteBit(false);
+ writer.WriteUe(0);
+ writer.WriteUe(context.h264_parameter_set.num_refidx_l0_default_active);
+ writer.WriteUe(context.h264_parameter_set.num_refidx_l1_default_active);
+ writer.WriteBit(((context.h264_parameter_set.flags >> 2) & 1) != 0);
+ writer.WriteU(static_cast<s32>((context.h264_parameter_set.flags >> 32) & 0x3), 2);
+ s32 pic_init_qp = static_cast<s32>((context.h264_parameter_set.flags >> 16) & 0x3f);
+ pic_init_qp = (pic_init_qp << 26) >> 26;
+ writer.WriteSe(pic_init_qp);
+ writer.WriteSe(0);
+ s32 chroma_qp_index_offset =
+ static_cast<s32>((context.h264_parameter_set.flags >> 22) & 0x1f);
+ chroma_qp_index_offset = (chroma_qp_index_offset << 27) >> 27;
+
+ writer.WriteSe(chroma_qp_index_offset);
+ writer.WriteBit(context.h264_parameter_set.deblocking_filter_control_flag != 0);
+ writer.WriteBit(((context.h264_parameter_set.flags >> 3) & 1) != 0);
+ writer.WriteBit(context.h264_parameter_set.redundant_pic_count_flag != 0);
+ writer.WriteBit(context.h264_parameter_set.transform_8x8_mode_flag != 0);
+
+ writer.WriteBit(true);
+
+ for (s32 index = 0; index < 6; index++) {
+ writer.WriteBit(true);
+ const auto matrix_x4 =
+ std::vector<u8>(context.scaling_matrix_4.begin(), context.scaling_matrix_4.end());
+ writer.WriteScalingList(matrix_x4, index * 16, 16);
+ }
+
+ if (context.h264_parameter_set.transform_8x8_mode_flag) {
+ for (s32 index = 0; index < 2; index++) {
+ writer.WriteBit(true);
+ const auto matrix_x8 = std::vector<u8>(context.scaling_matrix_8.begin(),
+ context.scaling_matrix_8.end());
+
+ writer.WriteScalingList(matrix_x8, index * 64, 64);
+ }
+ }
+
+ s32 chroma_qp_index_offset2 =
+ static_cast<s32>((context.h264_parameter_set.flags >> 27) & 0x1f);
+ chroma_qp_index_offset2 = (chroma_qp_index_offset2 << 27) >> 27;
+
+ writer.WriteSe(chroma_qp_index_offset2);
+
+ writer.End();
+
+ const auto& encoded_header = writer.GetByteArray();
+ frame.resize(encoded_header.size() + context.frame_data_size);
+ std::memcpy(frame.data(), encoded_header.data(), encoded_header.size());
+
+ gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset,
+ frame.data() + encoded_header.size(),
+ context.frame_data_size);
+ }
+
+ return frame;
+}
+
+H264BitWriter::H264BitWriter() = default;
+
+H264BitWriter::~H264BitWriter() = default;
+
+void H264BitWriter::WriteU(s32 value, s32 value_sz) {
+ WriteBits(value, value_sz);
+}
+
+void H264BitWriter::WriteSe(s32 value) {
+ WriteExpGolombCodedInt(value);
+}
+
+void H264BitWriter::WriteUe(u32 value) {
+ WriteExpGolombCodedUInt(value);
+}
+
+void H264BitWriter::End() {
+ WriteBit(true);
+ Flush();
+}
+
+void H264BitWriter::WriteBit(bool state) {
+ WriteBits(state ? 1 : 0, 1);
+}
+
+void H264BitWriter::WriteScalingList(const std::vector<u8>& list, s32 start, s32 count) {
+ std::vector<u8> scan(count);
+ if (count == 16) {
+ std::memcpy(scan.data(), zig_zag_scan.data(), scan.size());
+ } else {
+ std::memcpy(scan.data(), zig_zag_direct.data(), scan.size());
+ }
+ u8 last_scale = 8;
+
+ for (s32 index = 0; index < count; index++) {
+ const u8 value = list[start + scan[index]];
+ const s32 delta_scale = static_cast<s32>(value - last_scale);
+
+ WriteSe(delta_scale);
+
+ last_scale = value;
+ }
+}
+
+std::vector<u8>& H264BitWriter::GetByteArray() {
+ return byte_array;
+}
+
+const std::vector<u8>& H264BitWriter::GetByteArray() const {
+ return byte_array;
+}
+
+void H264BitWriter::WriteBits(s32 value, s32 bit_count) {
+ s32 value_pos = 0;
+
+ s32 remaining = bit_count;
+
+ while (remaining > 0) {
+ s32 copy_size = remaining;
+
+ const s32 free_bits = GetFreeBufferBits();
+
+ if (copy_size > free_bits) {
+ copy_size = free_bits;
+ }
+
+ const s32 mask = (1 << copy_size) - 1;
+
+ const s32 src_shift = (bit_count - value_pos) - copy_size;
+ const s32 dst_shift = (buffer_size - buffer_pos) - copy_size;
+
+ buffer |= ((value >> src_shift) & mask) << dst_shift;
+
+ value_pos += copy_size;
+ buffer_pos += copy_size;
+ remaining -= copy_size;
+ }
+}
+
+void H264BitWriter::WriteExpGolombCodedInt(s32 value) {
+ const s32 sign = value <= 0 ? 0 : 1;
+ if (value < 0) {
+ value = -value;
+ }
+ value = (value << 1) - sign;
+ WriteExpGolombCodedUInt(value);
+}
+
+void H264BitWriter::WriteExpGolombCodedUInt(u32 value) {
+ const s32 size = 32 - Common::CountLeadingZeroes32(static_cast<s32>(value + 1));
+ WriteBits(1, size);
+
+ value -= (1U << (size - 1)) - 1;
+ WriteBits(static_cast<s32>(value), size - 1);
+}
+
+s32 H264BitWriter::GetFreeBufferBits() {
+ if (buffer_pos == buffer_size) {
+ Flush();
+ }
+
+ return buffer_size - buffer_pos;
+}
+
+void H264BitWriter::Flush() {
+ if (buffer_pos == 0) {
+ return;
+ }
+ byte_array.push_back(static_cast<u8>(buffer));
+
+ buffer = 0;
+ buffer_pos = 0;
+}
+} // namespace Tegra::Decoder
diff --git a/src/video_core/command_classes/codecs/h264.h b/src/video_core/command_classes/codecs/h264.h
new file mode 100644
index 000000000..273449495
--- /dev/null
+++ b/src/video_core/command_classes/codecs/h264.h
@@ -0,0 +1,118 @@
+// MIT License
+//
+// Copyright (c) Ryujinx Team and Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#pragma once
+
+#include <vector>
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+#include "video_core/command_classes/nvdec_common.h"
+
+namespace Tegra {
+class GPU;
+namespace Decoder {
+
+class H264BitWriter {
+public:
+ H264BitWriter();
+ ~H264BitWriter();
+
+ /// The following Write methods are based on clause 9.1 in the H.264 specification.
+ /// WriteSe and WriteUe write in the Exp-Golomb-coded syntax
+ void WriteU(s32 value, s32 value_sz);
+ void WriteSe(s32 value);
+ void WriteUe(u32 value);
+
+ /// Finalize the bitstream
+ void End();
+
+ /// append a bit to the stream, equivalent value to the state parameter
+ void WriteBit(bool state);
+
+ /// Based on section 7.3.2.1.1.1 and Table 7-4 in the H.264 specification
+ /// Writes the scaling matrices of the sream
+ void WriteScalingList(const std::vector<u8>& list, s32 start, s32 count);
+
+ /// Return the bitstream as a vector.
+ [[nodiscard]] std::vector<u8>& GetByteArray();
+ [[nodiscard]] const std::vector<u8>& GetByteArray() const;
+
+private:
+ void WriteBits(s32 value, s32 bit_count);
+ void WriteExpGolombCodedInt(s32 value);
+ void WriteExpGolombCodedUInt(u32 value);
+ [[nodiscard]] s32 GetFreeBufferBits();
+ void Flush();
+
+ s32 buffer_size{8};
+
+ s32 buffer{};
+ s32 buffer_pos{};
+ std::vector<u8> byte_array;
+};
+
+class H264 {
+public:
+ explicit H264(GPU& gpu);
+ ~H264();
+
+ /// Compose the H264 header of the frame for FFmpeg decoding
+ [[nodiscard]] const std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state,
+ bool is_first_frame = false);
+
+private:
+ struct H264ParameterSet {
+ u32 log2_max_pic_order_cnt{};
+ u32 delta_pic_order_always_zero_flag{};
+ u32 frame_mbs_only_flag{};
+ u32 pic_width_in_mbs{};
+ u32 pic_height_in_map_units{};
+ INSERT_PADDING_WORDS(1);
+ u32 entropy_coding_mode_flag{};
+ u32 bottom_field_pic_order_flag{};
+ u32 num_refidx_l0_default_active{};
+ u32 num_refidx_l1_default_active{};
+ u32 deblocking_filter_control_flag{};
+ u32 redundant_pic_count_flag{};
+ u32 transform_8x8_mode_flag{};
+ INSERT_PADDING_WORDS(9);
+ u64 flags{};
+ u32 frame_number{};
+ u32 frame_number2{};
+ };
+ static_assert(sizeof(H264ParameterSet) == 0x68, "H264ParameterSet is an invalid size");
+
+ struct H264DecoderContext {
+ INSERT_PADDING_BYTES(0x48);
+ u32 frame_data_size{};
+ INSERT_PADDING_BYTES(0xc);
+ H264ParameterSet h264_parameter_set{};
+ INSERT_PADDING_BYTES(0x100);
+ std::array<u8, 0x60> scaling_matrix_4;
+ std::array<u8, 0x80> scaling_matrix_8;
+ };
+ static_assert(sizeof(H264DecoderContext) == 0x2a0, "H264DecoderContext is an invalid size");
+
+ std::vector<u8> frame;
+ GPU& gpu;
+};
+
+} // namespace Decoder
+} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/vp9.cpp b/src/video_core/command_classes/codecs/vp9.cpp
new file mode 100644
index 000000000..ab44fdc9e
--- /dev/null
+++ b/src/video_core/command_classes/codecs/vp9.cpp
@@ -0,0 +1,1040 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstring> // for std::memcpy
+#include <numeric>
+#include "video_core/command_classes/codecs/vp9.h"
+#include "video_core/gpu.h"
+#include "video_core/memory_manager.h"
+
+namespace Tegra::Decoder {
+namespace {
+// Default compressed header probabilities once frame context resets
+constexpr Vp9EntropyProbs default_probs{
+ .y_mode_prob{
+ 65, 32, 18, 144, 162, 194, 41, 51, 98, 132, 68, 18, 165, 217, 196, 45, 40, 78,
+ 173, 80, 19, 176, 240, 193, 64, 35, 46, 221, 135, 38, 194, 248, 121, 96, 85, 29,
+ },
+ .partition_prob{
+ 199, 122, 141, 0, 147, 63, 159, 0, 148, 133, 118, 0, 121, 104, 114, 0,
+ 174, 73, 87, 0, 92, 41, 83, 0, 82, 99, 50, 0, 53, 39, 39, 0,
+ 177, 58, 59, 0, 68, 26, 63, 0, 52, 79, 25, 0, 17, 14, 12, 0,
+ 222, 34, 30, 0, 72, 16, 44, 0, 58, 32, 12, 0, 10, 7, 6, 0,
+ },
+ .coef_probs{
+ 195, 29, 183, 0, 84, 49, 136, 0, 8, 42, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 31, 107, 169, 0, 35, 99, 159, 0, 17, 82, 140, 0, 8, 66, 114, 0,
+ 2, 44, 76, 0, 1, 19, 32, 0, 40, 132, 201, 0, 29, 114, 187, 0, 13, 91, 157, 0,
+ 7, 75, 127, 0, 3, 58, 95, 0, 1, 28, 47, 0, 69, 142, 221, 0, 42, 122, 201, 0,
+ 15, 91, 159, 0, 6, 67, 121, 0, 1, 42, 77, 0, 1, 17, 31, 0, 102, 148, 228, 0,
+ 67, 117, 204, 0, 17, 82, 154, 0, 6, 59, 114, 0, 2, 39, 75, 0, 1, 15, 29, 0,
+ 156, 57, 233, 0, 119, 57, 212, 0, 58, 48, 163, 0, 29, 40, 124, 0, 12, 30, 81, 0,
+ 3, 12, 31, 0, 191, 107, 226, 0, 124, 117, 204, 0, 25, 99, 155, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 29, 148, 210, 0, 37, 126, 194, 0, 8, 93, 157, 0,
+ 2, 68, 118, 0, 1, 39, 69, 0, 1, 17, 33, 0, 41, 151, 213, 0, 27, 123, 193, 0,
+ 3, 82, 144, 0, 1, 58, 105, 0, 1, 32, 60, 0, 1, 13, 26, 0, 59, 159, 220, 0,
+ 23, 126, 198, 0, 4, 88, 151, 0, 1, 66, 114, 0, 1, 38, 71, 0, 1, 18, 34, 0,
+ 114, 136, 232, 0, 51, 114, 207, 0, 11, 83, 155, 0, 3, 56, 105, 0, 1, 33, 65, 0,
+ 1, 17, 34, 0, 149, 65, 234, 0, 121, 57, 215, 0, 61, 49, 166, 0, 28, 36, 114, 0,
+ 12, 25, 76, 0, 3, 16, 42, 0, 214, 49, 220, 0, 132, 63, 188, 0, 42, 65, 137, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 137, 221, 0, 104, 131, 216, 0,
+ 49, 111, 192, 0, 21, 87, 155, 0, 2, 49, 87, 0, 1, 16, 28, 0, 89, 163, 230, 0,
+ 90, 137, 220, 0, 29, 100, 183, 0, 10, 70, 135, 0, 2, 42, 81, 0, 1, 17, 33, 0,
+ 108, 167, 237, 0, 55, 133, 222, 0, 15, 97, 179, 0, 4, 72, 135, 0, 1, 45, 85, 0,
+ 1, 19, 38, 0, 124, 146, 240, 0, 66, 124, 224, 0, 17, 88, 175, 0, 4, 58, 122, 0,
+ 1, 36, 75, 0, 1, 18, 37, 0, 141, 79, 241, 0, 126, 70, 227, 0, 66, 58, 182, 0,
+ 30, 44, 136, 0, 12, 34, 96, 0, 2, 20, 47, 0, 229, 99, 249, 0, 143, 111, 235, 0,
+ 46, 109, 192, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 82, 158, 236, 0,
+ 94, 146, 224, 0, 25, 117, 191, 0, 9, 87, 149, 0, 3, 56, 99, 0, 1, 33, 57, 0,
+ 83, 167, 237, 0, 68, 145, 222, 0, 10, 103, 177, 0, 2, 72, 131, 0, 1, 41, 79, 0,
+ 1, 20, 39, 0, 99, 167, 239, 0, 47, 141, 224, 0, 10, 104, 178, 0, 2, 73, 133, 0,
+ 1, 44, 85, 0, 1, 22, 47, 0, 127, 145, 243, 0, 71, 129, 228, 0, 17, 93, 177, 0,
+ 3, 61, 124, 0, 1, 41, 84, 0, 1, 21, 52, 0, 157, 78, 244, 0, 140, 72, 231, 0,
+ 69, 58, 184, 0, 31, 44, 137, 0, 14, 38, 105, 0, 8, 23, 61, 0, 125, 34, 187, 0,
+ 52, 41, 133, 0, 6, 31, 56, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 37, 109, 153, 0, 51, 102, 147, 0, 23, 87, 128, 0, 8, 67, 101, 0, 1, 41, 63, 0,
+ 1, 19, 29, 0, 31, 154, 185, 0, 17, 127, 175, 0, 6, 96, 145, 0, 2, 73, 114, 0,
+ 1, 51, 82, 0, 1, 28, 45, 0, 23, 163, 200, 0, 10, 131, 185, 0, 2, 93, 148, 0,
+ 1, 67, 111, 0, 1, 41, 69, 0, 1, 14, 24, 0, 29, 176, 217, 0, 12, 145, 201, 0,
+ 3, 101, 156, 0, 1, 69, 111, 0, 1, 39, 63, 0, 1, 14, 23, 0, 57, 192, 233, 0,
+ 25, 154, 215, 0, 6, 109, 167, 0, 3, 78, 118, 0, 1, 48, 69, 0, 1, 21, 29, 0,
+ 202, 105, 245, 0, 108, 106, 216, 0, 18, 90, 144, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 33, 172, 219, 0, 64, 149, 206, 0, 14, 117, 177, 0, 5, 90, 141, 0,
+ 2, 61, 95, 0, 1, 37, 57, 0, 33, 179, 220, 0, 11, 140, 198, 0, 1, 89, 148, 0,
+ 1, 60, 104, 0, 1, 33, 57, 0, 1, 12, 21, 0, 30, 181, 221, 0, 8, 141, 198, 0,
+ 1, 87, 145, 0, 1, 58, 100, 0, 1, 31, 55, 0, 1, 12, 20, 0, 32, 186, 224, 0,
+ 7, 142, 198, 0, 1, 86, 143, 0, 1, 58, 100, 0, 1, 31, 55, 0, 1, 12, 22, 0,
+ 57, 192, 227, 0, 20, 143, 204, 0, 3, 96, 154, 0, 1, 68, 112, 0, 1, 42, 69, 0,
+ 1, 19, 32, 0, 212, 35, 215, 0, 113, 47, 169, 0, 29, 48, 105, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 74, 129, 203, 0, 106, 120, 203, 0, 49, 107, 178, 0,
+ 19, 84, 144, 0, 4, 50, 84, 0, 1, 15, 25, 0, 71, 172, 217, 0, 44, 141, 209, 0,
+ 15, 102, 173, 0, 6, 76, 133, 0, 2, 51, 89, 0, 1, 24, 42, 0, 64, 185, 231, 0,
+ 31, 148, 216, 0, 8, 103, 175, 0, 3, 74, 131, 0, 1, 46, 81, 0, 1, 18, 30, 0,
+ 65, 196, 235, 0, 25, 157, 221, 0, 5, 105, 174, 0, 1, 67, 120, 0, 1, 38, 69, 0,
+ 1, 15, 30, 0, 65, 204, 238, 0, 30, 156, 224, 0, 7, 107, 177, 0, 2, 70, 124, 0,
+ 1, 42, 73, 0, 1, 18, 34, 0, 225, 86, 251, 0, 144, 104, 235, 0, 42, 99, 181, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 175, 239, 0, 112, 165, 229, 0,
+ 29, 136, 200, 0, 12, 103, 162, 0, 6, 77, 123, 0, 2, 53, 84, 0, 75, 183, 239, 0,
+ 30, 155, 221, 0, 3, 106, 171, 0, 1, 74, 128, 0, 1, 44, 76, 0, 1, 17, 28, 0,
+ 73, 185, 240, 0, 27, 159, 222, 0, 2, 107, 172, 0, 1, 75, 127, 0, 1, 42, 73, 0,
+ 1, 17, 29, 0, 62, 190, 238, 0, 21, 159, 222, 0, 2, 107, 172, 0, 1, 72, 122, 0,
+ 1, 40, 71, 0, 1, 18, 32, 0, 61, 199, 240, 0, 27, 161, 226, 0, 4, 113, 180, 0,
+ 1, 76, 129, 0, 1, 46, 80, 0, 1, 23, 41, 0, 7, 27, 153, 0, 5, 30, 95, 0,
+ 1, 16, 30, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 75, 127, 0,
+ 57, 75, 124, 0, 27, 67, 108, 0, 10, 54, 86, 0, 1, 33, 52, 0, 1, 12, 18, 0,
+ 43, 125, 151, 0, 26, 108, 148, 0, 7, 83, 122, 0, 2, 59, 89, 0, 1, 38, 60, 0,
+ 1, 17, 27, 0, 23, 144, 163, 0, 13, 112, 154, 0, 2, 75, 117, 0, 1, 50, 81, 0,
+ 1, 31, 51, 0, 1, 14, 23, 0, 18, 162, 185, 0, 6, 123, 171, 0, 1, 78, 125, 0,
+ 1, 51, 86, 0, 1, 31, 54, 0, 1, 14, 23, 0, 15, 199, 227, 0, 3, 150, 204, 0,
+ 1, 91, 146, 0, 1, 55, 95, 0, 1, 30, 53, 0, 1, 11, 20, 0, 19, 55, 240, 0,
+ 19, 59, 196, 0, 3, 52, 105, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 41, 166, 207, 0, 104, 153, 199, 0, 31, 123, 181, 0, 14, 101, 152, 0, 5, 72, 106, 0,
+ 1, 36, 52, 0, 35, 176, 211, 0, 12, 131, 190, 0, 2, 88, 144, 0, 1, 60, 101, 0,
+ 1, 36, 60, 0, 1, 16, 28, 0, 28, 183, 213, 0, 8, 134, 191, 0, 1, 86, 142, 0,
+ 1, 56, 96, 0, 1, 30, 53, 0, 1, 12, 20, 0, 20, 190, 215, 0, 4, 135, 192, 0,
+ 1, 84, 139, 0, 1, 53, 91, 0, 1, 28, 49, 0, 1, 11, 20, 0, 13, 196, 216, 0,
+ 2, 137, 192, 0, 1, 86, 143, 0, 1, 57, 99, 0, 1, 32, 56, 0, 1, 13, 24, 0,
+ 211, 29, 217, 0, 96, 47, 156, 0, 22, 43, 87, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 78, 120, 193, 0, 111, 116, 186, 0, 46, 102, 164, 0, 15, 80, 128, 0,
+ 2, 49, 76, 0, 1, 18, 28, 0, 71, 161, 203, 0, 42, 132, 192, 0, 10, 98, 150, 0,
+ 3, 69, 109, 0, 1, 44, 70, 0, 1, 18, 29, 0, 57, 186, 211, 0, 30, 140, 196, 0,
+ 4, 93, 146, 0, 1, 62, 102, 0, 1, 38, 65, 0, 1, 16, 27, 0, 47, 199, 217, 0,
+ 14, 145, 196, 0, 1, 88, 142, 0, 1, 57, 98, 0, 1, 36, 62, 0, 1, 15, 26, 0,
+ 26, 219, 229, 0, 5, 155, 207, 0, 1, 94, 151, 0, 1, 60, 104, 0, 1, 36, 62, 0,
+ 1, 16, 28, 0, 233, 29, 248, 0, 146, 47, 220, 0, 43, 52, 140, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 100, 163, 232, 0, 179, 161, 222, 0, 63, 142, 204, 0,
+ 37, 113, 174, 0, 26, 89, 137, 0, 18, 68, 97, 0, 85, 181, 230, 0, 32, 146, 209, 0,
+ 7, 100, 164, 0, 3, 71, 121, 0, 1, 45, 77, 0, 1, 18, 30, 0, 65, 187, 230, 0,
+ 20, 148, 207, 0, 2, 97, 159, 0, 1, 68, 116, 0, 1, 40, 70, 0, 1, 14, 29, 0,
+ 40, 194, 227, 0, 8, 147, 204, 0, 1, 94, 155, 0, 1, 65, 112, 0, 1, 39, 66, 0,
+ 1, 14, 26, 0, 16, 208, 228, 0, 3, 151, 207, 0, 1, 98, 160, 0, 1, 67, 117, 0,
+ 1, 41, 74, 0, 1, 17, 31, 0, 17, 38, 140, 0, 7, 34, 80, 0, 1, 17, 29, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 37, 75, 128, 0, 41, 76, 128, 0,
+ 26, 66, 116, 0, 12, 52, 94, 0, 2, 32, 55, 0, 1, 10, 16, 0, 50, 127, 154, 0,
+ 37, 109, 152, 0, 16, 82, 121, 0, 5, 59, 85, 0, 1, 35, 54, 0, 1, 13, 20, 0,
+ 40, 142, 167, 0, 17, 110, 157, 0, 2, 71, 112, 0, 1, 44, 72, 0, 1, 27, 45, 0,
+ 1, 11, 17, 0, 30, 175, 188, 0, 9, 124, 169, 0, 1, 74, 116, 0, 1, 48, 78, 0,
+ 1, 30, 49, 0, 1, 11, 18, 0, 10, 222, 223, 0, 2, 150, 194, 0, 1, 83, 128, 0,
+ 1, 48, 79, 0, 1, 27, 45, 0, 1, 11, 17, 0, 36, 41, 235, 0, 29, 36, 193, 0,
+ 10, 27, 111, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 85, 165, 222, 0,
+ 177, 162, 215, 0, 110, 135, 195, 0, 57, 113, 168, 0, 23, 83, 120, 0, 10, 49, 61, 0,
+ 85, 190, 223, 0, 36, 139, 200, 0, 5, 90, 146, 0, 1, 60, 103, 0, 1, 38, 65, 0,
+ 1, 18, 30, 0, 72, 202, 223, 0, 23, 141, 199, 0, 2, 86, 140, 0, 1, 56, 97, 0,
+ 1, 36, 61, 0, 1, 16, 27, 0, 55, 218, 225, 0, 13, 145, 200, 0, 1, 86, 141, 0,
+ 1, 57, 99, 0, 1, 35, 61, 0, 1, 13, 22, 0, 15, 235, 212, 0, 1, 132, 184, 0,
+ 1, 84, 139, 0, 1, 57, 97, 0, 1, 34, 56, 0, 1, 14, 23, 0, 181, 21, 201, 0,
+ 61, 37, 123, 0, 10, 38, 71, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 47, 106, 172, 0, 95, 104, 173, 0, 42, 93, 159, 0, 18, 77, 131, 0, 4, 50, 81, 0,
+ 1, 17, 23, 0, 62, 147, 199, 0, 44, 130, 189, 0, 28, 102, 154, 0, 18, 75, 115, 0,
+ 2, 44, 65, 0, 1, 12, 19, 0, 55, 153, 210, 0, 24, 130, 194, 0, 3, 93, 146, 0,
+ 1, 61, 97, 0, 1, 31, 50, 0, 1, 10, 16, 0, 49, 186, 223, 0, 17, 148, 204, 0,
+ 1, 96, 142, 0, 1, 53, 83, 0, 1, 26, 44, 0, 1, 11, 17, 0, 13, 217, 212, 0,
+ 2, 136, 180, 0, 1, 78, 124, 0, 1, 50, 83, 0, 1, 29, 49, 0, 1, 14, 23, 0,
+ 197, 13, 247, 0, 82, 17, 222, 0, 25, 17, 162, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 126, 186, 247, 0, 234, 191, 243, 0, 176, 177, 234, 0, 104, 158, 220, 0,
+ 66, 128, 186, 0, 55, 90, 137, 0, 111, 197, 242, 0, 46, 158, 219, 0, 9, 104, 171, 0,
+ 2, 65, 125, 0, 1, 44, 80, 0, 1, 17, 91, 0, 104, 208, 245, 0, 39, 168, 224, 0,
+ 3, 109, 162, 0, 1, 79, 124, 0, 1, 50, 102, 0, 1, 43, 102, 0, 84, 220, 246, 0,
+ 31, 177, 231, 0, 2, 115, 180, 0, 1, 79, 134, 0, 1, 55, 77, 0, 1, 60, 79, 0,
+ 43, 243, 240, 0, 8, 180, 217, 0, 1, 115, 166, 0, 1, 84, 121, 0, 1, 51, 67, 0,
+ 1, 16, 6, 0,
+ },
+ .switchable_interp_prob{235, 162, 36, 255, 34, 3, 149, 144},
+ .inter_mode_prob{
+ 2, 173, 34, 0, 7, 145, 85, 0, 7, 166, 63, 0, 7, 94,
+ 66, 0, 8, 64, 46, 0, 17, 81, 31, 0, 25, 29, 30, 0,
+ },
+ .intra_inter_prob{9, 102, 187, 225},
+ .comp_inter_prob{9, 102, 187, 225, 0},
+ .single_ref_prob{33, 16, 77, 74, 142, 142, 172, 170, 238, 247},
+ .comp_ref_prob{50, 126, 123, 221, 226},
+ .tx_32x32_prob{3, 136, 37, 5, 52, 13},
+ .tx_16x16_prob{20, 152, 15, 101},
+ .tx_8x8_prob{100, 66},
+ .skip_probs{192, 128, 64},
+ .joints{32, 64, 96},
+ .sign{128, 128},
+ .classes{
+ 224, 144, 192, 168, 192, 176, 192, 198, 198, 245,
+ 216, 128, 176, 160, 176, 176, 192, 198, 198, 208,
+ },
+ .class_0{216, 208},
+ .prob_bits{
+ 136, 140, 148, 160, 176, 192, 224, 234, 234, 240,
+ 136, 140, 148, 160, 176, 192, 224, 234, 234, 240,
+ },
+ .class_0_fr{128, 128, 64, 96, 112, 64, 128, 128, 64, 96, 112, 64},
+ .fr{64, 96, 64, 64, 96, 64},
+ .class_0_hp{160, 160},
+ .high_precision{128, 128},
+};
+
+constexpr std::array<s32, 256> norm_lut{
+ 0, 7, 6, 6, 5, 5, 5, 5, 4, 4, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3,
+ 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+};
+
+constexpr std::array<s32, 254> map_lut{
+ 20, 21, 22, 23, 24, 25, 0, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37,
+ 1, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 2, 50, 51, 52, 53, 54,
+ 55, 56, 57, 58, 59, 60, 61, 3, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72,
+ 73, 4, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 5, 86, 87, 88, 89,
+ 90, 91, 92, 93, 94, 95, 96, 97, 6, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
+ 108, 109, 7, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 8, 122, 123, 124,
+ 125, 126, 127, 128, 129, 130, 131, 132, 133, 9, 134, 135, 136, 137, 138, 139, 140, 141, 142,
+ 143, 144, 145, 10, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 11, 158, 159,
+ 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 12, 170, 171, 172, 173, 174, 175, 176, 177,
+ 178, 179, 180, 181, 13, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 14, 194,
+ 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 15, 206, 207, 208, 209, 210, 211, 212,
+ 213, 214, 215, 216, 217, 16, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 17,
+ 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 18, 242, 243, 244, 245, 246, 247,
+ 248, 249, 250, 251, 252, 253, 19,
+};
+
+// 6.2.14 Tile size calculation
+
+[[nodiscard]] s32 CalcMinLog2TileCols(s32 frame_width) {
+ const s32 sb64_cols = (frame_width + 63) / 64;
+ s32 min_log2 = 0;
+
+ while ((64 << min_log2) < sb64_cols) {
+ min_log2++;
+ }
+
+ return min_log2;
+}
+
+[[nodiscard]] s32 CalcMaxLog2TileCols(s32 frame_width) {
+ const s32 sb64_cols = (frame_width + 63) / 64;
+ s32 max_log2 = 1;
+
+ while ((sb64_cols >> max_log2) >= 4) {
+ max_log2++;
+ }
+
+ return max_log2 - 1;
+}
+
+// Recenters probability. Based on section 6.3.6 of VP9 Specification
+[[nodiscard]] s32 RecenterNonNeg(s32 new_prob, s32 old_prob) {
+ if (new_prob > old_prob * 2) {
+ return new_prob;
+ }
+
+ if (new_prob >= old_prob) {
+ return (new_prob - old_prob) * 2;
+ }
+
+ return (old_prob - new_prob) * 2 - 1;
+}
+
+// Adjusts old_prob depending on new_prob. Based on section 6.3.5 of VP9 Specification
+[[nodiscard]] s32 RemapProbability(s32 new_prob, s32 old_prob) {
+ new_prob--;
+ old_prob--;
+
+ std::size_t index{};
+
+ if (old_prob * 2 <= 0xff) {
+ index = static_cast<std::size_t>(std::max(0, RecenterNonNeg(new_prob, old_prob) - 1));
+ } else {
+ index = static_cast<std::size_t>(
+ std::max(0, RecenterNonNeg(0xff - 1 - new_prob, 0xff - 1 - old_prob) - 1));
+ }
+
+ return map_lut[index];
+}
+} // Anonymous namespace
+
+VP9::VP9(GPU& gpu) : gpu(gpu) {}
+
+VP9::~VP9() = default;
+
+void VP9::WriteProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob) {
+ const bool update = new_prob != old_prob;
+
+ writer.Write(update, diff_update_probability);
+
+ if (update) {
+ WriteProbabilityDelta(writer, new_prob, old_prob);
+ }
+}
+template <typename T, std::size_t N>
+void VP9::WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
+ const std::array<T, N>& old_prob) {
+ for (std::size_t offset = 0; offset < new_prob.size(); ++offset) {
+ WriteProbabilityUpdate(writer, new_prob[offset], old_prob[offset]);
+ }
+}
+
+template <typename T, std::size_t N>
+void VP9::WriteProbabilityUpdateAligned4(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
+ const std::array<T, N>& old_prob) {
+ for (std::size_t offset = 0; offset < new_prob.size(); offset += 4) {
+ WriteProbabilityUpdate(writer, new_prob[offset + 0], old_prob[offset + 0]);
+ WriteProbabilityUpdate(writer, new_prob[offset + 1], old_prob[offset + 1]);
+ WriteProbabilityUpdate(writer, new_prob[offset + 2], old_prob[offset + 2]);
+ }
+}
+
+void VP9::WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob) {
+ const int delta = RemapProbability(new_prob, old_prob);
+
+ EncodeTermSubExp(writer, delta);
+}
+
+void VP9::EncodeTermSubExp(VpxRangeEncoder& writer, s32 value) {
+ if (WriteLessThan(writer, value, 16)) {
+ writer.Write(value, 4);
+ } else if (WriteLessThan(writer, value, 32)) {
+ writer.Write(value - 16, 4);
+ } else if (WriteLessThan(writer, value, 64)) {
+ writer.Write(value - 32, 5);
+ } else {
+ value -= 64;
+
+ constexpr s32 size = 8;
+
+ const s32 mask = (1 << size) - 191;
+
+ const s32 delta = value - mask;
+
+ if (delta < 0) {
+ writer.Write(value, size - 1);
+ } else {
+ writer.Write(delta / 2 + mask, size - 1);
+ writer.Write(delta & 1, 1);
+ }
+ }
+}
+
+bool VP9::WriteLessThan(VpxRangeEncoder& writer, s32 value, s32 test) {
+ const bool is_lt = value < test;
+ writer.Write(!is_lt);
+ return is_lt;
+}
+
+void VP9::WriteCoefProbabilityUpdate(VpxRangeEncoder& writer, s32 tx_mode,
+ const std::array<u8, 2304>& new_prob,
+ const std::array<u8, 2304>& old_prob) {
+ // Note: There's 1 byte added on each packet for alignment,
+ // this byte is ignored when doing updates.
+ constexpr s32 block_bytes = 2 * 2 * 6 * 6 * 4;
+
+ const auto needs_update = [&](s32 base_index) -> bool {
+ s32 index = base_index;
+ for (s32 i = 0; i < 2; i++) {
+ for (s32 j = 0; j < 2; j++) {
+ for (s32 k = 0; k < 6; k++) {
+ for (s32 l = 0; l < 6; l++) {
+ if (new_prob[index + 0] != old_prob[index + 0] ||
+ new_prob[index + 1] != old_prob[index + 1] ||
+ new_prob[index + 2] != old_prob[index + 2]) {
+ return true;
+ }
+
+ index += 4;
+ }
+ }
+ }
+ }
+ return false;
+ };
+
+ for (s32 block_index = 0; block_index < 4; block_index++) {
+ const s32 base_index = block_index * block_bytes;
+ const bool update = needs_update(base_index);
+ writer.Write(update);
+
+ if (update) {
+ s32 index = base_index;
+ for (s32 i = 0; i < 2; i++) {
+ for (s32 j = 0; j < 2; j++) {
+ for (s32 k = 0; k < 6; k++) {
+ for (s32 l = 0; l < 6; l++) {
+ if (k != 0 || l < 3) {
+ WriteProbabilityUpdate(writer, new_prob[index + 0],
+ old_prob[index + 0]);
+ WriteProbabilityUpdate(writer, new_prob[index + 1],
+ old_prob[index + 1]);
+ WriteProbabilityUpdate(writer, new_prob[index + 2],
+ old_prob[index + 2]);
+ }
+ index += 4;
+ }
+ }
+ }
+ }
+ }
+
+ if (block_index == tx_mode) {
+ break;
+ }
+ }
+}
+
+void VP9::WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob) {
+ const bool update = new_prob != old_prob;
+ writer.Write(update, diff_update_probability);
+
+ if (update) {
+ writer.Write(new_prob >> 1, 7);
+ }
+}
+
+Vp9PictureInfo VP9::GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state) {
+ PictureInfo picture_info{};
+ gpu.MemoryManager().ReadBlock(state.picture_info_offset, &picture_info, sizeof(PictureInfo));
+ Vp9PictureInfo vp9_info = picture_info.Convert();
+
+ InsertEntropy(state.vp9_entropy_probs_offset, vp9_info.entropy);
+
+ // surface_luma_offset[0:3] contains the address of the reference frame offsets in the following
+ // order: last, golden, altref, current. It may be worthwhile to track the updates done here
+ // to avoid buffering frame data needed for reference frame updating in the header composition.
+ std::memcpy(vp9_info.frame_offsets.data(), state.surface_luma_offset.data(), 4 * sizeof(u64));
+
+ return vp9_info;
+}
+
+void VP9::InsertEntropy(u64 offset, Vp9EntropyProbs& dst) {
+ EntropyProbs entropy{};
+ gpu.MemoryManager().ReadBlock(offset, &entropy, sizeof(EntropyProbs));
+ entropy.Convert(dst);
+}
+
+Vp9FrameContainer VP9::GetCurrentFrame(const NvdecCommon::NvdecRegisters& state) {
+ Vp9FrameContainer frame{};
+ {
+ gpu.SyncGuestHost();
+ frame.info = GetVp9PictureInfo(state);
+
+ frame.bit_stream.resize(frame.info.bitstream_size);
+ gpu.MemoryManager().ReadBlock(state.frame_bitstream_offset, frame.bit_stream.data(),
+ frame.info.bitstream_size);
+ }
+ // Buffer two frames, saving the last show frame info
+ if (!next_next_frame.bit_stream.empty()) {
+ Vp9FrameContainer temp{
+ .info = frame.info,
+ .bit_stream = frame.bit_stream,
+ };
+ next_next_frame.info.show_frame = frame.info.last_frame_shown;
+ frame.info = next_next_frame.info;
+ frame.bit_stream = next_next_frame.bit_stream;
+ next_next_frame = std::move(temp);
+
+ if (!next_frame.bit_stream.empty()) {
+ Vp9FrameContainer temp2{
+ .info = frame.info,
+ .bit_stream = frame.bit_stream,
+ };
+ next_frame.info.show_frame = frame.info.last_frame_shown;
+ frame.info = next_frame.info;
+ frame.bit_stream = next_frame.bit_stream;
+ next_frame = std::move(temp2);
+ } else {
+ next_frame.info = frame.info;
+ next_frame.bit_stream = frame.bit_stream;
+ }
+ } else {
+ next_next_frame.info = frame.info;
+ next_next_frame.bit_stream = frame.bit_stream;
+ }
+ return frame;
+}
+
+std::vector<u8> VP9::ComposeCompressedHeader() {
+ VpxRangeEncoder writer{};
+
+ if (!current_frame_info.lossless) {
+ if (static_cast<u32>(current_frame_info.transform_mode) >= 3) {
+ writer.Write(3, 2);
+ writer.Write(current_frame_info.transform_mode == 4);
+ } else {
+ writer.Write(current_frame_info.transform_mode, 2);
+ }
+ }
+
+ if (current_frame_info.transform_mode == 4) {
+ // tx_mode_probs() in the spec
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.tx_8x8_prob,
+ prev_frame_probs.tx_8x8_prob);
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.tx_16x16_prob,
+ prev_frame_probs.tx_16x16_prob);
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.tx_32x32_prob,
+ prev_frame_probs.tx_32x32_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.tx_8x8_prob = current_frame_info.entropy.tx_8x8_prob;
+ prev_frame_probs.tx_16x16_prob = current_frame_info.entropy.tx_16x16_prob;
+ prev_frame_probs.tx_32x32_prob = current_frame_info.entropy.tx_32x32_prob;
+ }
+ }
+ // read_coef_probs() in the spec
+ WriteCoefProbabilityUpdate(writer, current_frame_info.transform_mode,
+ current_frame_info.entropy.coef_probs, prev_frame_probs.coef_probs);
+ // read_skip_probs() in the spec
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.skip_probs,
+ prev_frame_probs.skip_probs);
+
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.coef_probs = current_frame_info.entropy.coef_probs;
+ prev_frame_probs.skip_probs = current_frame_info.entropy.skip_probs;
+ }
+
+ if (!current_frame_info.intra_only) {
+ // read_inter_probs() in the spec
+ WriteProbabilityUpdateAligned4(writer, current_frame_info.entropy.inter_mode_prob,
+ prev_frame_probs.inter_mode_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.inter_mode_prob = current_frame_info.entropy.inter_mode_prob;
+ }
+
+ if (current_frame_info.interp_filter == 4) {
+ // read_interp_filter_probs() in the spec
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.switchable_interp_prob,
+ prev_frame_probs.switchable_interp_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.switchable_interp_prob =
+ current_frame_info.entropy.switchable_interp_prob;
+ }
+ }
+
+ // read_is_inter_probs() in the spec
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.intra_inter_prob,
+ prev_frame_probs.intra_inter_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.intra_inter_prob = current_frame_info.entropy.intra_inter_prob;
+ }
+ // frame_reference_mode() in the spec
+ if ((current_frame_info.ref_frame_sign_bias[1] & 1) !=
+ (current_frame_info.ref_frame_sign_bias[2] & 1) ||
+ (current_frame_info.ref_frame_sign_bias[1] & 1) !=
+ (current_frame_info.ref_frame_sign_bias[3] & 1)) {
+ if (current_frame_info.reference_mode >= 1) {
+ writer.Write(1, 1);
+ writer.Write(current_frame_info.reference_mode == 2);
+ } else {
+ writer.Write(0, 1);
+ }
+ }
+
+ // frame_reference_mode_probs() in the spec
+ if (current_frame_info.reference_mode == 2) {
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.comp_inter_prob,
+ prev_frame_probs.comp_inter_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.comp_inter_prob = current_frame_info.entropy.comp_inter_prob;
+ }
+ }
+
+ if (current_frame_info.reference_mode != 1) {
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.single_ref_prob,
+ prev_frame_probs.single_ref_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.single_ref_prob = current_frame_info.entropy.single_ref_prob;
+ }
+ }
+
+ if (current_frame_info.reference_mode != 0) {
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.comp_ref_prob,
+ prev_frame_probs.comp_ref_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.comp_ref_prob = current_frame_info.entropy.comp_ref_prob;
+ }
+ }
+
+ // read_y_mode_probs
+ for (std::size_t index = 0; index < current_frame_info.entropy.y_mode_prob.size();
+ ++index) {
+ WriteProbabilityUpdate(writer, current_frame_info.entropy.y_mode_prob[index],
+ prev_frame_probs.y_mode_prob[index]);
+ }
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.y_mode_prob = current_frame_info.entropy.y_mode_prob;
+ }
+ // read_partition_probs
+ WriteProbabilityUpdateAligned4(writer, current_frame_info.entropy.partition_prob,
+ prev_frame_probs.partition_prob);
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.partition_prob = current_frame_info.entropy.partition_prob;
+ }
+
+ // mv_probs
+ for (s32 i = 0; i < 3; i++) {
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.joints[i],
+ prev_frame_probs.joints[i]);
+ }
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.joints = current_frame_info.entropy.joints;
+ }
+
+ for (s32 i = 0; i < 2; i++) {
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.sign[i],
+ prev_frame_probs.sign[i]);
+
+ for (s32 j = 0; j < 10; j++) {
+ const int index = i * 10 + j;
+
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.classes[index],
+ prev_frame_probs.classes[index]);
+ }
+
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.class_0[i],
+ prev_frame_probs.class_0[i]);
+
+ for (s32 j = 0; j < 10; j++) {
+ const int index = i * 10 + j;
+
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.prob_bits[index],
+ prev_frame_probs.prob_bits[index]);
+ }
+ }
+
+ for (s32 i = 0; i < 2; i++) {
+ for (s32 j = 0; j < 2; j++) {
+ for (s32 k = 0; k < 3; k++) {
+ const int index = i * 2 * 3 + j * 3 + k;
+
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.class_0_fr[index],
+ prev_frame_probs.class_0_fr[index]);
+ }
+ }
+
+ for (s32 j = 0; j < 3; j++) {
+ const int index = i * 3 + j;
+
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.fr[index],
+ prev_frame_probs.fr[index]);
+ }
+ }
+
+ if (current_frame_info.allow_high_precision_mv) {
+ for (s32 index = 0; index < 2; index++) {
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.class_0_hp[index],
+ prev_frame_probs.class_0_hp[index]);
+ WriteMvProbabilityUpdate(writer, current_frame_info.entropy.high_precision[index],
+ prev_frame_probs.high_precision[index]);
+ }
+ }
+
+ // save previous probs
+ if (current_frame_info.show_frame && !current_frame_info.is_key_frame) {
+ prev_frame_probs.sign = current_frame_info.entropy.sign;
+ prev_frame_probs.classes = current_frame_info.entropy.classes;
+ prev_frame_probs.class_0 = current_frame_info.entropy.class_0;
+ prev_frame_probs.prob_bits = current_frame_info.entropy.prob_bits;
+ prev_frame_probs.class_0_fr = current_frame_info.entropy.class_0_fr;
+ prev_frame_probs.fr = current_frame_info.entropy.fr;
+ prev_frame_probs.class_0_hp = current_frame_info.entropy.class_0_hp;
+ prev_frame_probs.high_precision = current_frame_info.entropy.high_precision;
+ }
+ }
+
+ writer.End();
+ return writer.GetBuffer();
+}
+
+VpxBitStreamWriter VP9::ComposeUncompressedHeader() {
+ VpxBitStreamWriter uncomp_writer{};
+
+ uncomp_writer.WriteU(2, 2); // Frame marker.
+ uncomp_writer.WriteU(0, 2); // Profile.
+ uncomp_writer.WriteBit(false); // Show existing frame.
+ uncomp_writer.WriteBit(!current_frame_info.is_key_frame); // is key frame?
+ uncomp_writer.WriteBit(current_frame_info.show_frame); // show frame?
+ uncomp_writer.WriteBit(current_frame_info.error_resilient_mode); // error reslience
+
+ if (current_frame_info.is_key_frame) {
+ uncomp_writer.WriteU(frame_sync_code, 24);
+ uncomp_writer.WriteU(0, 3); // Color space.
+ uncomp_writer.WriteU(0, 1); // Color range.
+ uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16);
+ uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16);
+ uncomp_writer.WriteBit(false); // Render and frame size different.
+
+ // Reset context
+ prev_frame_probs = default_probs;
+ swap_next_golden = false;
+ loop_filter_ref_deltas.fill(0);
+ loop_filter_mode_deltas.fill(0);
+
+ // allow frames offsets to stabilize before checking for golden frames
+ grace_period = 4;
+
+ // On key frames, all frame slots are set to the current frame,
+ // so the value of the selected slot doesn't really matter.
+ frame_ctxs.fill({current_frame_number, false, default_probs});
+
+ // intra only, meaning the frame can be recreated with no other references
+ current_frame_info.intra_only = true;
+
+ } else {
+
+ if (!current_frame_info.show_frame) {
+ uncomp_writer.WriteBit(current_frame_info.intra_only);
+ if (!current_frame_info.last_frame_was_key) {
+ swap_next_golden = !swap_next_golden;
+ }
+ } else {
+ current_frame_info.intra_only = false;
+ }
+ if (!current_frame_info.error_resilient_mode) {
+ uncomp_writer.WriteU(0, 2); // Reset frame context.
+ }
+
+ // Last, Golden, Altref frames
+ std::array<s32, 3> ref_frame_index{0, 1, 2};
+
+ // Set when next frame is hidden
+ // altref and golden references are swapped
+ if (swap_next_golden) {
+ ref_frame_index = std::array<s32, 3>{0, 2, 1};
+ }
+
+ // update Last Frame
+ u64 refresh_frame_flags = 1;
+
+ // golden frame may refresh, determined if the next golden frame offset is changed
+ bool golden_refresh = false;
+ if (grace_period <= 0) {
+ for (s32 index = 1; index < 3; ++index) {
+ if (current_frame_info.frame_offsets[index] !=
+ next_frame.info.frame_offsets[index]) {
+ current_frame_info.refresh_frame[index] = true;
+ golden_refresh = true;
+ grace_period = 3;
+ }
+ }
+ }
+
+ if (current_frame_info.show_frame &&
+ (!next_frame.info.show_frame || next_frame.info.is_key_frame)) {
+ // Update golden frame
+ refresh_frame_flags = swap_next_golden ? 2 : 4;
+ }
+
+ if (!current_frame_info.show_frame) {
+ // Update altref
+ refresh_frame_flags = swap_next_golden ? 2 : 4;
+ } else if (golden_refresh) {
+ refresh_frame_flags = 3;
+ }
+
+ if (current_frame_info.intra_only) {
+ uncomp_writer.WriteU(frame_sync_code, 24);
+ uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8);
+ uncomp_writer.WriteU(current_frame_info.frame_size.width - 1, 16);
+ uncomp_writer.WriteU(current_frame_info.frame_size.height - 1, 16);
+ uncomp_writer.WriteBit(false); // Render and frame size different.
+ } else {
+ uncomp_writer.WriteU(static_cast<s32>(refresh_frame_flags), 8);
+
+ for (s32 index = 1; index < 4; index++) {
+ uncomp_writer.WriteU(ref_frame_index[index - 1], 3);
+ uncomp_writer.WriteU(current_frame_info.ref_frame_sign_bias[index], 1);
+ }
+
+ uncomp_writer.WriteBit(true); // Frame size with refs.
+ uncomp_writer.WriteBit(false); // Render and frame size different.
+ uncomp_writer.WriteBit(current_frame_info.allow_high_precision_mv);
+ uncomp_writer.WriteBit(current_frame_info.interp_filter == 4);
+
+ if (current_frame_info.interp_filter != 4) {
+ uncomp_writer.WriteU(current_frame_info.interp_filter, 2);
+ }
+ }
+ }
+
+ if (!current_frame_info.error_resilient_mode) {
+ uncomp_writer.WriteBit(true); // Refresh frame context. where do i get this info from?
+ uncomp_writer.WriteBit(true); // Frame parallel decoding mode.
+ }
+
+ int frame_ctx_idx = 0;
+ if (!current_frame_info.show_frame) {
+ frame_ctx_idx = 1;
+ }
+
+ uncomp_writer.WriteU(frame_ctx_idx, 2); // Frame context index.
+ prev_frame_probs =
+ frame_ctxs[frame_ctx_idx].probs; // reference probabilities for compressed header
+ frame_ctxs[frame_ctx_idx] = {current_frame_number, false, current_frame_info.entropy};
+
+ uncomp_writer.WriteU(current_frame_info.first_level, 6);
+ uncomp_writer.WriteU(current_frame_info.sharpness_level, 3);
+ uncomp_writer.WriteBit(current_frame_info.mode_ref_delta_enabled);
+
+ if (current_frame_info.mode_ref_delta_enabled) {
+ // check if ref deltas are different, update accordingly
+ std::array<bool, 4> update_loop_filter_ref_deltas;
+ std::array<bool, 2> update_loop_filter_mode_deltas;
+
+ bool loop_filter_delta_update = false;
+
+ for (std::size_t index = 0; index < current_frame_info.ref_deltas.size(); index++) {
+ const s8 old_deltas = loop_filter_ref_deltas[index];
+ const s8 new_deltas = current_frame_info.ref_deltas[index];
+ const bool differing_delta = old_deltas != new_deltas;
+
+ update_loop_filter_ref_deltas[index] = differing_delta;
+ loop_filter_delta_update |= differing_delta;
+ }
+
+ for (std::size_t index = 0; index < current_frame_info.mode_deltas.size(); index++) {
+ const s8 old_deltas = loop_filter_mode_deltas[index];
+ const s8 new_deltas = current_frame_info.mode_deltas[index];
+ const bool differing_delta = old_deltas != new_deltas;
+
+ update_loop_filter_mode_deltas[index] = differing_delta;
+ loop_filter_delta_update |= differing_delta;
+ }
+
+ uncomp_writer.WriteBit(loop_filter_delta_update);
+
+ if (loop_filter_delta_update) {
+ for (std::size_t index = 0; index < current_frame_info.ref_deltas.size(); index++) {
+ uncomp_writer.WriteBit(update_loop_filter_ref_deltas[index]);
+
+ if (update_loop_filter_ref_deltas[index]) {
+ uncomp_writer.WriteS(current_frame_info.ref_deltas[index], 6);
+ }
+ }
+
+ for (std::size_t index = 0; index < current_frame_info.mode_deltas.size(); index++) {
+ uncomp_writer.WriteBit(update_loop_filter_mode_deltas[index]);
+
+ if (update_loop_filter_mode_deltas[index]) {
+ uncomp_writer.WriteS(current_frame_info.mode_deltas[index], 6);
+ }
+ }
+ // save new deltas
+ loop_filter_ref_deltas = current_frame_info.ref_deltas;
+ loop_filter_mode_deltas = current_frame_info.mode_deltas;
+ }
+ }
+
+ uncomp_writer.WriteU(current_frame_info.base_q_index, 8);
+
+ uncomp_writer.WriteDeltaQ(current_frame_info.y_dc_delta_q);
+ uncomp_writer.WriteDeltaQ(current_frame_info.uv_dc_delta_q);
+ uncomp_writer.WriteDeltaQ(current_frame_info.uv_ac_delta_q);
+
+ uncomp_writer.WriteBit(false); // Segmentation enabled (TODO).
+
+ const s32 min_tile_cols_log2 = CalcMinLog2TileCols(current_frame_info.frame_size.width);
+ const s32 max_tile_cols_log2 = CalcMaxLog2TileCols(current_frame_info.frame_size.width);
+
+ const s32 tile_cols_log2_diff = current_frame_info.log2_tile_cols - min_tile_cols_log2;
+ const s32 tile_cols_log2_inc_mask = (1 << tile_cols_log2_diff) - 1;
+
+ // If it's less than the maximum, we need to add an extra 0 on the bitstream
+ // to indicate that it should stop reading.
+ if (current_frame_info.log2_tile_cols < max_tile_cols_log2) {
+ uncomp_writer.WriteU(tile_cols_log2_inc_mask << 1, tile_cols_log2_diff + 1);
+ } else {
+ uncomp_writer.WriteU(tile_cols_log2_inc_mask, tile_cols_log2_diff);
+ }
+
+ const bool tile_rows_log2_is_nonzero = current_frame_info.log2_tile_rows != 0;
+
+ uncomp_writer.WriteBit(tile_rows_log2_is_nonzero);
+
+ if (tile_rows_log2_is_nonzero) {
+ uncomp_writer.WriteBit(current_frame_info.log2_tile_rows > 1);
+ }
+
+ return uncomp_writer;
+}
+
+const std::vector<u8>& VP9::ComposeFrameHeader(NvdecCommon::NvdecRegisters& state) {
+ std::vector<u8> bitstream;
+ {
+ Vp9FrameContainer curr_frame = GetCurrentFrame(state);
+ current_frame_info = curr_frame.info;
+ bitstream = std::move(curr_frame.bit_stream);
+ }
+
+ // The uncompressed header routine sets PrevProb parameters needed for the compressed header
+ auto uncomp_writer = ComposeUncompressedHeader();
+ std::vector<u8> compressed_header = ComposeCompressedHeader();
+
+ uncomp_writer.WriteU(static_cast<s32>(compressed_header.size()), 16);
+ uncomp_writer.Flush();
+ std::vector<u8> uncompressed_header = uncomp_writer.GetByteArray();
+
+ // Write headers and frame to buffer
+ frame.resize(uncompressed_header.size() + compressed_header.size() + bitstream.size());
+ std::memcpy(frame.data(), uncompressed_header.data(), uncompressed_header.size());
+ std::memcpy(frame.data() + uncompressed_header.size(), compressed_header.data(),
+ compressed_header.size());
+ std::memcpy(frame.data() + uncompressed_header.size() + compressed_header.size(),
+ bitstream.data(), bitstream.size());
+
+ // keep track of frame number
+ current_frame_number++;
+ grace_period--;
+
+ // don't display hidden frames
+ hidden = !current_frame_info.show_frame;
+ return frame;
+}
+
+VpxRangeEncoder::VpxRangeEncoder() {
+ Write(false);
+}
+
+VpxRangeEncoder::~VpxRangeEncoder() = default;
+
+void VpxRangeEncoder::Write(s32 value, s32 value_size) {
+ for (s32 bit = value_size - 1; bit >= 0; bit--) {
+ Write(((value >> bit) & 1) != 0);
+ }
+}
+
+void VpxRangeEncoder::Write(bool bit) {
+ Write(bit, half_probability);
+}
+
+void VpxRangeEncoder::Write(bool bit, s32 probability) {
+ u32 local_range = range;
+ const u32 split = 1 + (((local_range - 1) * static_cast<u32>(probability)) >> 8);
+ local_range = split;
+
+ if (bit) {
+ low_value += split;
+ local_range = range - split;
+ }
+
+ s32 shift = norm_lut[local_range];
+ local_range <<= shift;
+ count += shift;
+
+ if (count >= 0) {
+ const s32 offset = shift - count;
+
+ if (((low_value << (offset - 1)) >> 31) != 0) {
+ const s32 current_pos = static_cast<s32>(base_stream.GetPosition());
+ base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
+ while (PeekByte() == 0xff) {
+ base_stream.WriteByte(0);
+
+ base_stream.Seek(-2, Common::SeekOrigin::FromCurrentPos);
+ }
+ base_stream.WriteByte(static_cast<u8>((PeekByte() + 1)));
+ base_stream.Seek(current_pos, Common::SeekOrigin::SetOrigin);
+ }
+ base_stream.WriteByte(static_cast<u8>((low_value >> (24 - offset))));
+
+ low_value <<= offset;
+ shift = count;
+ low_value &= 0xffffff;
+ count -= 8;
+ }
+
+ low_value <<= shift;
+ range = local_range;
+}
+
+void VpxRangeEncoder::End() {
+ for (std::size_t index = 0; index < 32; ++index) {
+ Write(false);
+ }
+}
+
+u8 VpxRangeEncoder::PeekByte() {
+ const u8 value = base_stream.ReadByte();
+ base_stream.Seek(-1, Common::SeekOrigin::FromCurrentPos);
+
+ return value;
+}
+
+VpxBitStreamWriter::VpxBitStreamWriter() = default;
+
+VpxBitStreamWriter::~VpxBitStreamWriter() = default;
+
+void VpxBitStreamWriter::WriteU(u32 value, u32 value_size) {
+ WriteBits(value, value_size);
+}
+
+void VpxBitStreamWriter::WriteS(s32 value, u32 value_size) {
+ const bool sign = value < 0;
+ if (sign) {
+ value = -value;
+ }
+
+ WriteBits(static_cast<u32>(value << 1) | (sign ? 1 : 0), value_size + 1);
+}
+
+void VpxBitStreamWriter::WriteDeltaQ(u32 value) {
+ const bool delta_coded = value != 0;
+ WriteBit(delta_coded);
+
+ if (delta_coded) {
+ WriteBits(value, 4);
+ }
+}
+
+void VpxBitStreamWriter::WriteBits(u32 value, u32 bit_count) {
+ s32 value_pos = 0;
+ s32 remaining = bit_count;
+
+ while (remaining > 0) {
+ s32 copy_size = remaining;
+
+ const s32 free = GetFreeBufferBits();
+
+ if (copy_size > free) {
+ copy_size = free;
+ }
+
+ const s32 mask = (1 << copy_size) - 1;
+
+ const s32 src_shift = (bit_count - value_pos) - copy_size;
+ const s32 dst_shift = (buffer_size - buffer_pos) - copy_size;
+
+ buffer |= ((value >> src_shift) & mask) << dst_shift;
+
+ value_pos += copy_size;
+ buffer_pos += copy_size;
+ remaining -= copy_size;
+ }
+}
+
+void VpxBitStreamWriter::WriteBit(bool state) {
+ WriteBits(state ? 1 : 0, 1);
+}
+
+s32 VpxBitStreamWriter::GetFreeBufferBits() {
+ if (buffer_pos == buffer_size) {
+ Flush();
+ }
+
+ return buffer_size - buffer_pos;
+}
+
+void VpxBitStreamWriter::Flush() {
+ if (buffer_pos == 0) {
+ return;
+ }
+ byte_array.push_back(static_cast<u8>(buffer));
+ buffer = 0;
+ buffer_pos = 0;
+}
+
+std::vector<u8>& VpxBitStreamWriter::GetByteArray() {
+ return byte_array;
+}
+
+const std::vector<u8>& VpxBitStreamWriter::GetByteArray() const {
+ return byte_array;
+}
+
+} // namespace Tegra::Decoder
diff --git a/src/video_core/command_classes/codecs/vp9.h b/src/video_core/command_classes/codecs/vp9.h
new file mode 100644
index 000000000..e2504512c
--- /dev/null
+++ b/src/video_core/command_classes/codecs/vp9.h
@@ -0,0 +1,196 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <vector>
+
+#include "common/common_types.h"
+#include "common/stream.h"
+#include "video_core/command_classes/codecs/vp9_types.h"
+#include "video_core/command_classes/nvdec_common.h"
+
+namespace Tegra {
+class GPU;
+enum class FrameType { KeyFrame = 0, InterFrame = 1 };
+namespace Decoder {
+
+/// The VpxRangeEncoder, and VpxBitStreamWriter classes are used to compose the
+/// VP9 header bitstreams.
+
+class VpxRangeEncoder {
+public:
+ VpxRangeEncoder();
+ ~VpxRangeEncoder();
+
+ VpxRangeEncoder(const VpxRangeEncoder&) = delete;
+ VpxRangeEncoder& operator=(const VpxRangeEncoder&) = delete;
+
+ VpxRangeEncoder(VpxRangeEncoder&&) = default;
+ VpxRangeEncoder& operator=(VpxRangeEncoder&&) = default;
+
+ /// Writes the rightmost value_size bits from value into the stream
+ void Write(s32 value, s32 value_size);
+
+ /// Writes a single bit with half probability
+ void Write(bool bit);
+
+ /// Writes a bit to the base_stream encoded with probability
+ void Write(bool bit, s32 probability);
+
+ /// Signal the end of the bitstream
+ void End();
+
+ [[nodiscard]] std::vector<u8>& GetBuffer() {
+ return base_stream.GetBuffer();
+ }
+
+ [[nodiscard]] const std::vector<u8>& GetBuffer() const {
+ return base_stream.GetBuffer();
+ }
+
+private:
+ u8 PeekByte();
+ Common::Stream base_stream{};
+ u32 low_value{};
+ u32 range{0xff};
+ s32 count{-24};
+ s32 half_probability{128};
+};
+
+class VpxBitStreamWriter {
+public:
+ VpxBitStreamWriter();
+ ~VpxBitStreamWriter();
+
+ VpxBitStreamWriter(const VpxBitStreamWriter&) = delete;
+ VpxBitStreamWriter& operator=(const VpxBitStreamWriter&) = delete;
+
+ VpxBitStreamWriter(VpxBitStreamWriter&&) = default;
+ VpxBitStreamWriter& operator=(VpxBitStreamWriter&&) = default;
+
+ /// Write an unsigned integer value
+ void WriteU(u32 value, u32 value_size);
+
+ /// Write a signed integer value
+ void WriteS(s32 value, u32 value_size);
+
+ /// Based on 6.2.10 of VP9 Spec, writes a delta coded value
+ void WriteDeltaQ(u32 value);
+
+ /// Write a single bit.
+ void WriteBit(bool state);
+
+ /// Pushes current buffer into buffer_array, resets buffer
+ void Flush();
+
+ /// Returns byte_array
+ [[nodiscard]] std::vector<u8>& GetByteArray();
+
+ /// Returns const byte_array
+ [[nodiscard]] const std::vector<u8>& GetByteArray() const;
+
+private:
+ /// Write bit_count bits from value into buffer
+ void WriteBits(u32 value, u32 bit_count);
+
+ /// Gets next available position in buffer, invokes Flush() if buffer is full
+ s32 GetFreeBufferBits();
+
+ s32 buffer_size{8};
+
+ s32 buffer{};
+ s32 buffer_pos{};
+ std::vector<u8> byte_array;
+};
+
+class VP9 {
+public:
+ explicit VP9(GPU& gpu);
+ ~VP9();
+
+ VP9(const VP9&) = delete;
+ VP9& operator=(const VP9&) = delete;
+
+ VP9(VP9&&) = default;
+ VP9& operator=(VP9&&) = delete;
+
+ /// Composes the VP9 frame from the GPU state information. Based on the official VP9 spec
+ /// documentation
+ [[nodiscard]] const std::vector<u8>& ComposeFrameHeader(NvdecCommon::NvdecRegisters& state);
+
+ /// Returns true if the most recent frame was a hidden frame.
+ [[nodiscard]] bool WasFrameHidden() const {
+ return hidden;
+ }
+
+private:
+ /// Generates compressed header probability updates in the bitstream writer
+ template <typename T, std::size_t N>
+ void WriteProbabilityUpdate(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
+ const std::array<T, N>& old_prob);
+
+ /// Generates compressed header probability updates in the bitstream writer
+ /// If probs are not equal, WriteProbabilityDelta is invoked
+ void WriteProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
+
+ /// Generates compressed header probability deltas in the bitstream writer
+ void WriteProbabilityDelta(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
+
+ /// Inverse of 6.3.4 Decode term subexp
+ void EncodeTermSubExp(VpxRangeEncoder& writer, s32 value);
+
+ /// Writes if the value is less than the test value
+ bool WriteLessThan(VpxRangeEncoder& writer, s32 value, s32 test);
+
+ /// Writes probability updates for the Coef probabilities
+ void WriteCoefProbabilityUpdate(VpxRangeEncoder& writer, s32 tx_mode,
+ const std::array<u8, 2304>& new_prob,
+ const std::array<u8, 2304>& old_prob);
+
+ /// Write probabilities for 4-byte aligned structures
+ template <typename T, std::size_t N>
+ void WriteProbabilityUpdateAligned4(VpxRangeEncoder& writer, const std::array<T, N>& new_prob,
+ const std::array<T, N>& old_prob);
+
+ /// Write motion vector probability updates. 6.3.17 in the spec
+ void WriteMvProbabilityUpdate(VpxRangeEncoder& writer, u8 new_prob, u8 old_prob);
+
+ /// Returns VP9 information from NVDEC provided offset and size
+ [[nodiscard]] Vp9PictureInfo GetVp9PictureInfo(const NvdecCommon::NvdecRegisters& state);
+
+ /// Read and convert NVDEC provided entropy probs to Vp9EntropyProbs struct
+ void InsertEntropy(u64 offset, Vp9EntropyProbs& dst);
+
+ /// Returns frame to be decoded after buffering
+ [[nodiscard]] Vp9FrameContainer GetCurrentFrame(const NvdecCommon::NvdecRegisters& state);
+
+ /// Use NVDEC providied information to compose the headers for the current frame
+ [[nodiscard]] std::vector<u8> ComposeCompressedHeader();
+ [[nodiscard]] VpxBitStreamWriter ComposeUncompressedHeader();
+
+ GPU& gpu;
+ std::vector<u8> frame;
+
+ std::array<s8, 4> loop_filter_ref_deltas{};
+ std::array<s8, 2> loop_filter_mode_deltas{};
+
+ bool hidden = false;
+ s64 current_frame_number = -2; // since we buffer 2 frames
+ s32 grace_period = 6; // frame offsets need to stabilize
+ std::array<FrameContexts, 4> frame_ctxs{};
+ Vp9FrameContainer next_frame{};
+ Vp9FrameContainer next_next_frame{};
+ bool swap_next_golden{};
+
+ Vp9PictureInfo current_frame_info{};
+ Vp9EntropyProbs prev_frame_probs{};
+
+ s32 diff_update_probability = 252;
+ s32 frame_sync_code = 0x498342;
+};
+
+} // namespace Decoder
+} // namespace Tegra
diff --git a/src/video_core/command_classes/codecs/vp9_types.h b/src/video_core/command_classes/codecs/vp9_types.h
new file mode 100644
index 000000000..4f0b05d22
--- /dev/null
+++ b/src/video_core/command_classes/codecs/vp9_types.h
@@ -0,0 +1,366 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <cstring>
+#include <vector>
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Tegra {
+class GPU;
+
+namespace Decoder {
+struct Vp9FrameDimensions {
+ s16 width{};
+ s16 height{};
+ s16 luma_pitch{};
+ s16 chroma_pitch{};
+};
+static_assert(sizeof(Vp9FrameDimensions) == 0x8, "Vp9 Vp9FrameDimensions is an invalid size");
+
+enum FrameFlags : u32 {
+ IsKeyFrame = 1 << 0,
+ LastFrameIsKeyFrame = 1 << 1,
+ FrameSizeChanged = 1 << 2,
+ ErrorResilientMode = 1 << 3,
+ LastShowFrame = 1 << 4,
+ IntraOnly = 1 << 5,
+};
+
+enum class MvJointType {
+ MvJointZero = 0, /* Zero vector */
+ MvJointHnzvz = 1, /* Vert zero, hor nonzero */
+ MvJointHzvnz = 2, /* Hor zero, vert nonzero */
+ MvJointHnzvnz = 3, /* Both components nonzero */
+};
+enum class MvClassType {
+ MvClass0 = 0, /* (0, 2] integer pel */
+ MvClass1 = 1, /* (2, 4] integer pel */
+ MvClass2 = 2, /* (4, 8] integer pel */
+ MvClass3 = 3, /* (8, 16] integer pel */
+ MvClass4 = 4, /* (16, 32] integer pel */
+ MvClass5 = 5, /* (32, 64] integer pel */
+ MvClass6 = 6, /* (64, 128] integer pel */
+ MvClass7 = 7, /* (128, 256] integer pel */
+ MvClass8 = 8, /* (256, 512] integer pel */
+ MvClass9 = 9, /* (512, 1024] integer pel */
+ MvClass10 = 10, /* (1024,2048] integer pel */
+};
+
+enum class BlockSize {
+ Block4x4 = 0,
+ Block4x8 = 1,
+ Block8x4 = 2,
+ Block8x8 = 3,
+ Block8x16 = 4,
+ Block16x8 = 5,
+ Block16x16 = 6,
+ Block16x32 = 7,
+ Block32x16 = 8,
+ Block32x32 = 9,
+ Block32x64 = 10,
+ Block64x32 = 11,
+ Block64x64 = 12,
+ BlockSizes = 13,
+ BlockInvalid = BlockSizes
+};
+
+enum class PredictionMode {
+ DcPred = 0, // Average of above and left pixels
+ VPred = 1, // Vertical
+ HPred = 2, // Horizontal
+ D45Pred = 3, // Directional 45 deg = round(arctan(1 / 1) * 180 / pi)
+ D135Pred = 4, // Directional 135 deg = 180 - 45
+ D117Pred = 5, // Directional 117 deg = 180 - 63
+ D153Pred = 6, // Directional 153 deg = 180 - 27
+ D207Pred = 7, // Directional 207 deg = 180 + 27
+ D63Pred = 8, // Directional 63 deg = round(arctan(2 / 1) * 180 / pi)
+ TmPred = 9, // True-motion
+ NearestMv = 10,
+ NearMv = 11,
+ ZeroMv = 12,
+ NewMv = 13,
+ MbModeCount = 14
+};
+
+enum class TxSize {
+ Tx4x4 = 0, // 4x4 transform
+ Tx8x8 = 1, // 8x8 transform
+ Tx16x16 = 2, // 16x16 transform
+ Tx32x32 = 3, // 32x32 transform
+ TxSizes = 4
+};
+
+enum class TxMode {
+ Only4X4 = 0, // Only 4x4 transform used
+ Allow8X8 = 1, // Allow block transform size up to 8x8
+ Allow16X16 = 2, // Allow block transform size up to 16x16
+ Allow32X32 = 3, // Allow block transform size up to 32x32
+ TxModeSelect = 4, // Transform specified for each block
+ TxModes = 5
+};
+
+enum class reference_mode {
+ SingleReference = 0,
+ CompoundReference = 1,
+ ReferenceModeSelect = 2,
+ ReferenceModes = 3
+};
+
+struct Segmentation {
+ u8 enabled{};
+ u8 update_map{};
+ u8 temporal_update{};
+ u8 abs_delta{};
+ std::array<u32, 8> feature_mask{};
+ std::array<std::array<s16, 4>, 8> feature_data{};
+};
+static_assert(sizeof(Segmentation) == 0x64, "Segmentation is an invalid size");
+
+struct LoopFilter {
+ u8 mode_ref_delta_enabled{};
+ std::array<s8, 4> ref_deltas{};
+ std::array<s8, 2> mode_deltas{};
+};
+static_assert(sizeof(LoopFilter) == 0x7, "LoopFilter is an invalid size");
+
+struct Vp9EntropyProbs {
+ std::array<u8, 36> y_mode_prob{};
+ std::array<u8, 64> partition_prob{};
+ std::array<u8, 2304> coef_probs{};
+ std::array<u8, 8> switchable_interp_prob{};
+ std::array<u8, 28> inter_mode_prob{};
+ std::array<u8, 4> intra_inter_prob{};
+ std::array<u8, 5> comp_inter_prob{};
+ std::array<u8, 10> single_ref_prob{};
+ std::array<u8, 5> comp_ref_prob{};
+ std::array<u8, 6> tx_32x32_prob{};
+ std::array<u8, 4> tx_16x16_prob{};
+ std::array<u8, 2> tx_8x8_prob{};
+ std::array<u8, 3> skip_probs{};
+ std::array<u8, 3> joints{};
+ std::array<u8, 2> sign{};
+ std::array<u8, 20> classes{};
+ std::array<u8, 2> class_0{};
+ std::array<u8, 20> prob_bits{};
+ std::array<u8, 12> class_0_fr{};
+ std::array<u8, 6> fr{};
+ std::array<u8, 2> class_0_hp{};
+ std::array<u8, 2> high_precision{};
+};
+static_assert(sizeof(Vp9EntropyProbs) == 0x9F4, "Vp9EntropyProbs is an invalid size");
+
+struct Vp9PictureInfo {
+ bool is_key_frame{};
+ bool intra_only{};
+ bool last_frame_was_key{};
+ bool frame_size_changed{};
+ bool error_resilient_mode{};
+ bool last_frame_shown{};
+ bool show_frame{};
+ std::array<s8, 4> ref_frame_sign_bias{};
+ s32 base_q_index{};
+ s32 y_dc_delta_q{};
+ s32 uv_dc_delta_q{};
+ s32 uv_ac_delta_q{};
+ bool lossless{};
+ s32 transform_mode{};
+ bool allow_high_precision_mv{};
+ s32 interp_filter{};
+ s32 reference_mode{};
+ s8 comp_fixed_ref{};
+ std::array<s8, 2> comp_var_ref{};
+ s32 log2_tile_cols{};
+ s32 log2_tile_rows{};
+ bool segment_enabled{};
+ bool segment_map_update{};
+ bool segment_map_temporal_update{};
+ s32 segment_abs_delta{};
+ std::array<u32, 8> segment_feature_enable{};
+ std::array<std::array<s16, 4>, 8> segment_feature_data{};
+ bool mode_ref_delta_enabled{};
+ bool use_prev_in_find_mv_refs{};
+ std::array<s8, 4> ref_deltas{};
+ std::array<s8, 2> mode_deltas{};
+ Vp9EntropyProbs entropy{};
+ Vp9FrameDimensions frame_size{};
+ u8 first_level{};
+ u8 sharpness_level{};
+ u32 bitstream_size{};
+ std::array<u64, 4> frame_offsets{};
+ std::array<bool, 4> refresh_frame{};
+};
+
+struct Vp9FrameContainer {
+ Vp9PictureInfo info{};
+ std::vector<u8> bit_stream;
+};
+
+struct PictureInfo {
+ INSERT_PADDING_WORDS(12);
+ u32 bitstream_size{};
+ INSERT_PADDING_WORDS(5);
+ Vp9FrameDimensions last_frame_size{};
+ Vp9FrameDimensions golden_frame_size{};
+ Vp9FrameDimensions alt_frame_size{};
+ Vp9FrameDimensions current_frame_size{};
+ u32 vp9_flags{};
+ std::array<s8, 4> ref_frame_sign_bias{};
+ u8 first_level{};
+ u8 sharpness_level{};
+ u8 base_q_index{};
+ u8 y_dc_delta_q{};
+ u8 uv_ac_delta_q{};
+ u8 uv_dc_delta_q{};
+ u8 lossless{};
+ u8 tx_mode{};
+ u8 allow_high_precision_mv{};
+ u8 interp_filter{};
+ u8 reference_mode{};
+ s8 comp_fixed_ref{};
+ std::array<s8, 2> comp_var_ref{};
+ u8 log2_tile_cols{};
+ u8 log2_tile_rows{};
+ Segmentation segmentation{};
+ LoopFilter loop_filter{};
+ INSERT_PADDING_BYTES(5);
+ u32 surface_params{};
+ INSERT_PADDING_WORDS(3);
+
+ [[nodiscard]] Vp9PictureInfo Convert() const {
+ return {
+ .is_key_frame = (vp9_flags & FrameFlags::IsKeyFrame) != 0,
+ .intra_only = (vp9_flags & FrameFlags::IntraOnly) != 0,
+ .last_frame_was_key = (vp9_flags & FrameFlags::LastFrameIsKeyFrame) != 0,
+ .frame_size_changed = (vp9_flags & FrameFlags::FrameSizeChanged) != 0,
+ .error_resilient_mode = (vp9_flags & FrameFlags::ErrorResilientMode) != 0,
+ .last_frame_shown = (vp9_flags & FrameFlags::LastShowFrame) != 0,
+ .ref_frame_sign_bias = ref_frame_sign_bias,
+ .base_q_index = base_q_index,
+ .y_dc_delta_q = y_dc_delta_q,
+ .uv_dc_delta_q = uv_dc_delta_q,
+ .uv_ac_delta_q = uv_ac_delta_q,
+ .lossless = lossless != 0,
+ .transform_mode = tx_mode,
+ .allow_high_precision_mv = allow_high_precision_mv != 0,
+ .interp_filter = interp_filter,
+ .reference_mode = reference_mode,
+ .comp_fixed_ref = comp_fixed_ref,
+ .comp_var_ref = comp_var_ref,
+ .log2_tile_cols = log2_tile_cols,
+ .log2_tile_rows = log2_tile_rows,
+ .segment_enabled = segmentation.enabled != 0,
+ .segment_map_update = segmentation.update_map != 0,
+ .segment_map_temporal_update = segmentation.temporal_update != 0,
+ .segment_abs_delta = segmentation.abs_delta,
+ .segment_feature_enable = segmentation.feature_mask,
+ .segment_feature_data = segmentation.feature_data,
+ .mode_ref_delta_enabled = loop_filter.mode_ref_delta_enabled != 0,
+ .use_prev_in_find_mv_refs = !(vp9_flags == (FrameFlags::ErrorResilientMode)) &&
+ !(vp9_flags == (FrameFlags::FrameSizeChanged)) &&
+ !(vp9_flags == (FrameFlags::IntraOnly)) &&
+ (vp9_flags == (FrameFlags::LastShowFrame)) &&
+ !(vp9_flags == (FrameFlags::LastFrameIsKeyFrame)),
+ .ref_deltas = loop_filter.ref_deltas,
+ .mode_deltas = loop_filter.mode_deltas,
+ .frame_size = current_frame_size,
+ .first_level = first_level,
+ .sharpness_level = sharpness_level,
+ .bitstream_size = bitstream_size,
+ };
+ }
+};
+static_assert(sizeof(PictureInfo) == 0x100, "PictureInfo is an invalid size");
+
+struct EntropyProbs {
+ INSERT_PADDING_BYTES(1024);
+ std::array<std::array<u8, 4>, 7> inter_mode_prob{};
+ std::array<u8, 4> intra_inter_prob{};
+ INSERT_PADDING_BYTES(80);
+ std::array<std::array<u8, 1>, 2> tx_8x8_prob{};
+ std::array<std::array<u8, 2>, 2> tx_16x16_prob{};
+ std::array<std::array<u8, 3>, 2> tx_32x32_prob{};
+ std::array<u8, 4> y_mode_prob_e8{};
+ std::array<std::array<u8, 8>, 4> y_mode_prob_e0e7{};
+ INSERT_PADDING_BYTES(64);
+ std::array<std::array<u8, 4>, 16> partition_prob{};
+ INSERT_PADDING_BYTES(10);
+ std::array<std::array<u8, 2>, 4> switchable_interp_prob{};
+ std::array<u8, 5> comp_inter_prob{};
+ std::array<u8, 4> skip_probs{};
+ std::array<u8, 3> joints{};
+ std::array<u8, 2> sign{};
+ std::array<std::array<u8, 1>, 2> class_0{};
+ std::array<std::array<u8, 3>, 2> fr{};
+ std::array<u8, 2> class_0_hp{};
+ std::array<u8, 2> high_precision{};
+ std::array<std::array<u8, 10>, 2> classes{};
+ std::array<std::array<std::array<u8, 3>, 2>, 2> class_0_fr{};
+ std::array<std::array<u8, 10>, 2> pred_bits{};
+ std::array<std::array<u8, 2>, 5> single_ref_prob{};
+ std::array<u8, 5> comp_ref_prob{};
+ INSERT_PADDING_BYTES(17);
+ std::array<std::array<std::array<std::array<std::array<std::array<u8, 4>, 6>, 6>, 2>, 2>, 4>
+ coef_probs{};
+
+ void Convert(Vp9EntropyProbs& fc) {
+ std::memcpy(fc.inter_mode_prob.data(), inter_mode_prob.data(), fc.inter_mode_prob.size());
+
+ std::memcpy(fc.intra_inter_prob.data(), intra_inter_prob.data(),
+ fc.intra_inter_prob.size());
+
+ std::memcpy(fc.tx_8x8_prob.data(), tx_8x8_prob.data(), fc.tx_8x8_prob.size());
+ std::memcpy(fc.tx_16x16_prob.data(), tx_16x16_prob.data(), fc.tx_16x16_prob.size());
+ std::memcpy(fc.tx_32x32_prob.data(), tx_32x32_prob.data(), fc.tx_32x32_prob.size());
+
+ for (s32 i = 0; i < 4; i++) {
+ for (s32 j = 0; j < 9; j++) {
+ fc.y_mode_prob[j + 9 * i] = j < 8 ? y_mode_prob_e0e7[i][j] : y_mode_prob_e8[i];
+ }
+ }
+
+ std::memcpy(fc.partition_prob.data(), partition_prob.data(), fc.partition_prob.size());
+
+ std::memcpy(fc.switchable_interp_prob.data(), switchable_interp_prob.data(),
+ fc.switchable_interp_prob.size());
+ std::memcpy(fc.comp_inter_prob.data(), comp_inter_prob.data(), fc.comp_inter_prob.size());
+ std::memcpy(fc.skip_probs.data(), skip_probs.data(), fc.skip_probs.size());
+
+ std::memcpy(fc.joints.data(), joints.data(), fc.joints.size());
+
+ std::memcpy(fc.sign.data(), sign.data(), fc.sign.size());
+ std::memcpy(fc.class_0.data(), class_0.data(), fc.class_0.size());
+ std::memcpy(fc.fr.data(), fr.data(), fc.fr.size());
+ std::memcpy(fc.class_0_hp.data(), class_0_hp.data(), fc.class_0_hp.size());
+ std::memcpy(fc.high_precision.data(), high_precision.data(), fc.high_precision.size());
+ std::memcpy(fc.classes.data(), classes.data(), fc.classes.size());
+ std::memcpy(fc.class_0_fr.data(), class_0_fr.data(), fc.class_0_fr.size());
+ std::memcpy(fc.prob_bits.data(), pred_bits.data(), fc.prob_bits.size());
+ std::memcpy(fc.single_ref_prob.data(), single_ref_prob.data(), fc.single_ref_prob.size());
+ std::memcpy(fc.comp_ref_prob.data(), comp_ref_prob.data(), fc.comp_ref_prob.size());
+
+ std::memcpy(fc.coef_probs.data(), coef_probs.data(), fc.coef_probs.size());
+ }
+};
+static_assert(sizeof(EntropyProbs) == 0xEA0, "EntropyProbs is an invalid size");
+
+enum class Ref { Last, Golden, AltRef };
+
+struct RefPoolElement {
+ s64 frame{};
+ Ref ref{};
+ bool refresh{};
+};
+
+struct FrameContexts {
+ s64 from{};
+ bool adapted{};
+ Vp9EntropyProbs probs{};
+};
+
+}; // namespace Decoder
+}; // namespace Tegra
diff --git a/src/video_core/command_classes/host1x.cpp b/src/video_core/command_classes/host1x.cpp
new file mode 100644
index 000000000..c4dd4881a
--- /dev/null
+++ b/src/video_core/command_classes/host1x.cpp
@@ -0,0 +1,39 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "video_core/command_classes/host1x.h"
+#include "video_core/gpu.h"
+
+Tegra::Host1x::Host1x(GPU& gpu_) : gpu(gpu_) {}
+
+Tegra::Host1x::~Host1x() = default;
+
+void Tegra::Host1x::StateWrite(u32 offset, u32 arguments) {
+ u8* const state_offset = reinterpret_cast<u8*>(&state) + offset * sizeof(u32);
+ std::memcpy(state_offset, &arguments, sizeof(u32));
+}
+
+void Tegra::Host1x::ProcessMethod(Method method, const std::vector<u32>& arguments) {
+ StateWrite(static_cast<u32>(method), arguments[0]);
+ switch (method) {
+ case Method::WaitSyncpt:
+ Execute(arguments[0]);
+ break;
+ case Method::LoadSyncptPayload32:
+ syncpoint_value = arguments[0];
+ break;
+ case Method::WaitSyncpt32:
+ Execute(arguments[0]);
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Host1x method 0x{:X}", static_cast<u32>(method));
+ break;
+ }
+}
+
+void Tegra::Host1x::Execute(u32 data) {
+ // This method waits on a valid syncpoint.
+ // TODO: Implement when proper Async is in place
+}
diff --git a/src/video_core/command_classes/host1x.h b/src/video_core/command_classes/host1x.h
new file mode 100644
index 000000000..013eaa0c1
--- /dev/null
+++ b/src/video_core/command_classes/host1x.h
@@ -0,0 +1,78 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Tegra {
+class GPU;
+class Nvdec;
+
+class Host1x {
+public:
+ struct Host1xClassRegisters {
+ u32 incr_syncpt{};
+ u32 incr_syncpt_ctrl{};
+ u32 incr_syncpt_error{};
+ INSERT_PADDING_WORDS(5);
+ u32 wait_syncpt{};
+ u32 wait_syncpt_base{};
+ u32 wait_syncpt_incr{};
+ u32 load_syncpt_base{};
+ u32 incr_syncpt_base{};
+ u32 clear{};
+ u32 wait{};
+ u32 wait_with_interrupt{};
+ u32 delay_use{};
+ u32 tick_count_high{};
+ u32 tick_count_low{};
+ u32 tick_ctrl{};
+ INSERT_PADDING_WORDS(23);
+ u32 ind_ctrl{};
+ u32 ind_off2{};
+ u32 ind_off{};
+ std::array<u32, 31> ind_data{};
+ INSERT_PADDING_WORDS(1);
+ u32 load_syncpoint_payload32{};
+ u32 stall_ctrl{};
+ u32 wait_syncpt32{};
+ u32 wait_syncpt_base32{};
+ u32 load_syncpt_base32{};
+ u32 incr_syncpt_base32{};
+ u32 stall_count_high{};
+ u32 stall_count_low{};
+ u32 xref_ctrl{};
+ u32 channel_xref_high{};
+ u32 channel_xref_low{};
+ };
+ static_assert(sizeof(Host1xClassRegisters) == 0x164, "Host1xClassRegisters is an invalid size");
+
+ enum class Method : u32 {
+ WaitSyncpt = offsetof(Host1xClassRegisters, wait_syncpt) / 4,
+ LoadSyncptPayload32 = offsetof(Host1xClassRegisters, load_syncpoint_payload32) / 4,
+ WaitSyncpt32 = offsetof(Host1xClassRegisters, wait_syncpt32) / 4,
+ };
+
+ explicit Host1x(GPU& gpu);
+ ~Host1x();
+
+ /// Writes the method into the state, Invoke Execute() if encountered
+ void ProcessMethod(Method method, const std::vector<u32>& arguments);
+
+private:
+ /// For Host1x, execute is waiting on a syncpoint previously written into the state
+ void Execute(u32 data);
+
+ /// Write argument into the provided offset
+ void StateWrite(u32 offset, u32 arguments);
+
+ u32 syncpoint_value{};
+ Host1xClassRegisters state{};
+ GPU& gpu;
+};
+
+} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec.cpp b/src/video_core/command_classes/nvdec.cpp
new file mode 100644
index 000000000..8ca7a7b06
--- /dev/null
+++ b/src/video_core/command_classes/nvdec.cpp
@@ -0,0 +1,52 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "common/assert.h"
+#include "video_core/command_classes/nvdec.h"
+#include "video_core/gpu.h"
+
+namespace Tegra {
+
+Nvdec::Nvdec(GPU& gpu_) : gpu(gpu_), codec(std::make_unique<Codec>(gpu)) {}
+
+Nvdec::~Nvdec() = default;
+
+void Nvdec::ProcessMethod(Method method, const std::vector<u32>& arguments) {
+ if (method == Method::SetVideoCodec) {
+ codec->StateWrite(static_cast<u32>(method), arguments[0]);
+ } else {
+ codec->StateWrite(static_cast<u32>(method), static_cast<u64>(arguments[0]) << 8);
+ }
+
+ switch (method) {
+ case Method::SetVideoCodec:
+ codec->SetTargetCodec(static_cast<NvdecCommon::VideoCodec>(arguments[0]));
+ break;
+ case Method::Execute:
+ Execute();
+ break;
+ }
+}
+
+AVFrame* Nvdec::GetFrame() {
+ return codec->GetCurrentFrame();
+}
+
+const AVFrame* Nvdec::GetFrame() const {
+ return codec->GetCurrentFrame();
+}
+
+void Nvdec::Execute() {
+ switch (codec->GetCurrentCodec()) {
+ case NvdecCommon::VideoCodec::H264:
+ case NvdecCommon::VideoCodec::Vp9:
+ codec->Decode();
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unknown codec {}", static_cast<u32>(codec->GetCurrentCodec()));
+ break;
+ }
+}
+
+} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec.h b/src/video_core/command_classes/nvdec.h
new file mode 100644
index 000000000..eec4443f9
--- /dev/null
+++ b/src/video_core/command_classes/nvdec.h
@@ -0,0 +1,39 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include "common/common_types.h"
+#include "video_core/command_classes/codecs/codec.h"
+
+namespace Tegra {
+class GPU;
+
+class Nvdec {
+public:
+ enum class Method : u32 {
+ SetVideoCodec = 0x80,
+ Execute = 0xc0,
+ };
+
+ explicit Nvdec(GPU& gpu);
+ ~Nvdec();
+
+ /// Writes the method into the state, Invoke Execute() if encountered
+ void ProcessMethod(Method method, const std::vector<u32>& arguments);
+
+ /// Return most recently decoded frame
+ [[nodiscard]] AVFrame* GetFrame();
+ [[nodiscard]] const AVFrame* GetFrame() const;
+
+private:
+ /// Invoke codec to decode a frame
+ void Execute();
+
+ GPU& gpu;
+ std::unique_ptr<Codec> codec;
+};
+} // namespace Tegra
diff --git a/src/video_core/command_classes/nvdec_common.h b/src/video_core/command_classes/nvdec_common.h
new file mode 100644
index 000000000..01b5e086d
--- /dev/null
+++ b/src/video_core/command_classes/nvdec_common.h
@@ -0,0 +1,48 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include "common/common_funcs.h"
+#include "common/common_types.h"
+
+namespace Tegra::NvdecCommon {
+
+struct NvdecRegisters {
+ INSERT_PADDING_WORDS(256);
+ u64 set_codec_id{};
+ INSERT_PADDING_WORDS(254);
+ u64 set_platform_id{};
+ u64 picture_info_offset{};
+ u64 frame_bitstream_offset{};
+ u64 frame_number{};
+ u64 h264_slice_data_offsets{};
+ u64 h264_mv_dump_offset{};
+ INSERT_PADDING_WORDS(6);
+ u64 frame_stats_offset{};
+ u64 h264_last_surface_luma_offset{};
+ u64 h264_last_surface_chroma_offset{};
+ std::array<u64, 17> surface_luma_offset{};
+ std::array<u64, 17> surface_chroma_offset{};
+ INSERT_PADDING_WORDS(132);
+ u64 vp9_entropy_probs_offset{};
+ u64 vp9_backward_updates_offset{};
+ u64 vp9_last_frame_segmap_offset{};
+ u64 vp9_curr_frame_segmap_offset{};
+ INSERT_PADDING_WORDS(2);
+ u64 vp9_last_frame_mvs_offset{};
+ u64 vp9_curr_frame_mvs_offset{};
+ INSERT_PADDING_WORDS(2);
+};
+static_assert(sizeof(NvdecRegisters) == (0xBC0), "NvdecRegisters is incorrect size");
+
+enum class VideoCodec : u32 {
+ None = 0x0,
+ H264 = 0x3,
+ Vp8 = 0x5,
+ H265 = 0x7,
+ Vp9 = 0x9,
+};
+
+} // namespace Tegra::NvdecCommon
diff --git a/src/video_core/command_classes/sync_manager.cpp b/src/video_core/command_classes/sync_manager.cpp
new file mode 100644
index 000000000..19dc9e0ab
--- /dev/null
+++ b/src/video_core/command_classes/sync_manager.cpp
@@ -0,0 +1,60 @@
+// MIT License
+//
+// Copyright (c) Ryujinx Team and Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#include <algorithm>
+#include "sync_manager.h"
+#include "video_core/gpu.h"
+
+namespace Tegra {
+SyncptIncrManager::SyncptIncrManager(GPU& gpu_) : gpu(gpu_) {}
+SyncptIncrManager::~SyncptIncrManager() = default;
+
+void SyncptIncrManager::Increment(u32 id) {
+ increments.emplace_back(0, 0, id, true);
+ IncrementAllDone();
+}
+
+u32 SyncptIncrManager::IncrementWhenDone(u32 class_id, u32 id) {
+ const u32 handle = current_id++;
+ increments.emplace_back(handle, class_id, id);
+ return handle;
+}
+
+void SyncptIncrManager::SignalDone(u32 handle) {
+ const auto done_incr =
+ std::find_if(increments.begin(), increments.end(),
+ [handle](const SyncptIncr& incr) { return incr.id == handle; });
+ if (done_incr != increments.cend()) {
+ done_incr->complete = true;
+ }
+ IncrementAllDone();
+}
+
+void SyncptIncrManager::IncrementAllDone() {
+ std::size_t done_count = 0;
+ for (; done_count < increments.size(); ++done_count) {
+ if (!increments[done_count].complete) {
+ break;
+ }
+ gpu.IncrementSyncPoint(increments[done_count].syncpt_id);
+ }
+ increments.erase(increments.begin(), increments.begin() + done_count);
+}
+} // namespace Tegra
diff --git a/src/video_core/command_classes/sync_manager.h b/src/video_core/command_classes/sync_manager.h
new file mode 100644
index 000000000..2c321ec58
--- /dev/null
+++ b/src/video_core/command_classes/sync_manager.h
@@ -0,0 +1,64 @@
+// MIT License
+//
+// Copyright (c) Ryujinx Team and Contributors
+//
+// Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
+// associated documentation files (the "Software"), to deal in the Software without restriction,
+// including without limitation the rights to use, copy, modify, merge, publish, distribute,
+// sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+// furnished to do so, subject to the following conditions:
+//
+// The above copyright notice and this permission notice shall be included in all copies or
+// substantial portions of the Software.
+//
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT
+// NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
+#pragma once
+
+#include <mutex>
+#include <vector>
+#include "common/common_types.h"
+
+namespace Tegra {
+class GPU;
+struct SyncptIncr {
+ u32 id;
+ u32 class_id;
+ u32 syncpt_id;
+ bool complete;
+
+ SyncptIncr(u32 id_, u32 class_id_, u32 syncpt_id_, bool done = false)
+ : id(id_), class_id(class_id_), syncpt_id(syncpt_id_), complete(done) {}
+};
+
+class SyncptIncrManager {
+public:
+ explicit SyncptIncrManager(GPU& gpu);
+ ~SyncptIncrManager();
+
+ /// Add syncpoint id and increment all
+ void Increment(u32 id);
+
+ /// Returns a handle to increment later
+ u32 IncrementWhenDone(u32 class_id, u32 id);
+
+ /// IncrememntAllDone, including handle
+ void SignalDone(u32 handle);
+
+ /// Increment all sequential pending increments that are already done.
+ void IncrementAllDone();
+
+private:
+ std::vector<SyncptIncr> increments;
+ std::mutex increment_lock;
+ u32 current_id{};
+
+ GPU& gpu;
+};
+
+} // namespace Tegra
diff --git a/src/video_core/command_classes/vic.cpp b/src/video_core/command_classes/vic.cpp
new file mode 100644
index 000000000..5b52da277
--- /dev/null
+++ b/src/video_core/command_classes/vic.cpp
@@ -0,0 +1,180 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include "common/assert.h"
+#include "video_core/command_classes/nvdec.h"
+#include "video_core/command_classes/vic.h"
+#include "video_core/engines/maxwell_3d.h"
+#include "video_core/gpu.h"
+#include "video_core/memory_manager.h"
+#include "video_core/texture_cache/surface_params.h"
+
+extern "C" {
+#include <libswscale/swscale.h>
+}
+
+namespace Tegra {
+
+Vic::Vic(GPU& gpu_, std::shared_ptr<Nvdec> nvdec_processor_)
+ : gpu(gpu_), nvdec_processor(std::move(nvdec_processor_)) {}
+Vic::~Vic() = default;
+
+void Vic::VicStateWrite(u32 offset, u32 arguments) {
+ u8* const state_offset = reinterpret_cast<u8*>(&vic_state) + offset * sizeof(u32);
+ std::memcpy(state_offset, &arguments, sizeof(u32));
+}
+
+void Vic::ProcessMethod(Method method, const std::vector<u32>& arguments) {
+ LOG_DEBUG(HW_GPU, "Vic method 0x{:X}", static_cast<u32>(method));
+ VicStateWrite(static_cast<u32>(method), arguments[0]);
+ const u64 arg = static_cast<u64>(arguments[0]) << 8;
+ switch (method) {
+ case Method::Execute:
+ Execute();
+ break;
+ case Method::SetConfigStructOffset:
+ config_struct_address = arg;
+ break;
+ case Method::SetOutputSurfaceLumaOffset:
+ output_surface_luma_address = arg;
+ break;
+ case Method::SetOutputSurfaceChromaUOffset:
+ output_surface_chroma_u_address = arg;
+ break;
+ case Method::SetOutputSurfaceChromaVOffset:
+ output_surface_chroma_v_address = arg;
+ break;
+ default:
+ break;
+ }
+}
+
+void Vic::Execute() {
+ if (output_surface_luma_address == 0) {
+ LOG_ERROR(Service_NVDRV, "VIC Luma address not set. Recieved 0x{:X}",
+ vic_state.output_surface.luma_offset);
+ return;
+ }
+ const VicConfig config{gpu.MemoryManager().Read<u64>(config_struct_address + 0x20)};
+ const VideoPixelFormat pixel_format =
+ static_cast<VideoPixelFormat>(config.pixel_format.Value());
+ switch (pixel_format) {
+ case VideoPixelFormat::BGRA8:
+ case VideoPixelFormat::RGBA8: {
+ LOG_TRACE(Service_NVDRV, "Writing RGB Frame");
+ const auto* frame = nvdec_processor->GetFrame();
+
+ if (!frame || frame->width == 0 || frame->height == 0) {
+ return;
+ }
+ if (scaler_ctx == nullptr || frame->width != scaler_width ||
+ frame->height != scaler_height) {
+ const AVPixelFormat target_format =
+ (pixel_format == VideoPixelFormat::RGBA8) ? AV_PIX_FMT_RGBA : AV_PIX_FMT_BGRA;
+
+ sws_freeContext(scaler_ctx);
+ scaler_ctx = nullptr;
+
+ // FFmpeg returns all frames in YUV420, convert it into expected format
+ scaler_ctx =
+ sws_getContext(frame->width, frame->height, AV_PIX_FMT_YUV420P, frame->width,
+ frame->height, target_format, 0, nullptr, nullptr, nullptr);
+
+ scaler_width = frame->width;
+ scaler_height = frame->height;
+ }
+ // Get Converted frame
+ const std::size_t linear_size = frame->width * frame->height * 4;
+
+ using AVMallocPtr = std::unique_ptr<u8, decltype(&av_free)>;
+ AVMallocPtr converted_frame_buffer{static_cast<u8*>(av_malloc(linear_size)), av_free};
+
+ const int converted_stride{frame->width * 4};
+ u8* const converted_frame_buf_addr{converted_frame_buffer.get()};
+
+ sws_scale(scaler_ctx, frame->data, frame->linesize, 0, frame->height,
+ &converted_frame_buf_addr, &converted_stride);
+
+ const u32 blk_kind = static_cast<u32>(config.block_linear_kind);
+ if (blk_kind != 0) {
+ // swizzle pitch linear to block linear
+ const u32 block_height = static_cast<u32>(config.block_linear_height_log2);
+ const auto size = Tegra::Texture::CalculateSize(true, 4, frame->width, frame->height, 1,
+ block_height, 0);
+ std::vector<u8> swizzled_data(size);
+ Tegra::Texture::CopySwizzledData(frame->width, frame->height, 1, 4, 4,
+ swizzled_data.data(), converted_frame_buffer.get(),
+ false, block_height, 0, 1);
+
+ gpu.MemoryManager().WriteBlock(output_surface_luma_address, swizzled_data.data(), size);
+ gpu.Maxwell3D().OnMemoryWrite();
+ } else {
+ // send pitch linear frame
+ gpu.MemoryManager().WriteBlock(output_surface_luma_address, converted_frame_buf_addr,
+ linear_size);
+ gpu.Maxwell3D().OnMemoryWrite();
+ }
+ break;
+ }
+ case VideoPixelFormat::Yuv420: {
+ LOG_TRACE(Service_NVDRV, "Writing YUV420 Frame");
+
+ const auto* frame = nvdec_processor->GetFrame();
+
+ if (!frame || frame->width == 0 || frame->height == 0) {
+ return;
+ }
+
+ const std::size_t surface_width = config.surface_width_minus1 + 1;
+ const std::size_t surface_height = config.surface_height_minus1 + 1;
+ const std::size_t half_width = surface_width / 2;
+ const std::size_t half_height = config.surface_height_minus1 / 2;
+ const std::size_t aligned_width = (surface_width + 0xff) & ~0xff;
+
+ const auto* luma_ptr = frame->data[0];
+ const auto* chroma_b_ptr = frame->data[1];
+ const auto* chroma_r_ptr = frame->data[2];
+ const auto stride = frame->linesize[0];
+ const auto half_stride = frame->linesize[1];
+
+ std::vector<u8> luma_buffer(aligned_width * surface_height);
+ std::vector<u8> chroma_buffer(aligned_width * half_height);
+
+ // Populate luma buffer
+ for (std::size_t y = 0; y < surface_height - 1; ++y) {
+ std::size_t src = y * stride;
+ std::size_t dst = y * aligned_width;
+
+ std::size_t size = surface_width;
+
+ for (std::size_t offset = 0; offset < size; ++offset) {
+ luma_buffer[dst + offset] = luma_ptr[src + offset];
+ }
+ }
+ gpu.MemoryManager().WriteBlock(output_surface_luma_address, luma_buffer.data(),
+ luma_buffer.size());
+
+ // Populate chroma buffer from both channels with interleaving.
+ for (std::size_t y = 0; y < half_height; ++y) {
+ std::size_t src = y * half_stride;
+ std::size_t dst = y * aligned_width;
+
+ for (std::size_t x = 0; x < half_width; ++x) {
+ chroma_buffer[dst + x * 2] = chroma_b_ptr[src + x];
+ chroma_buffer[dst + x * 2 + 1] = chroma_r_ptr[src + x];
+ }
+ }
+ gpu.MemoryManager().WriteBlock(output_surface_chroma_u_address, chroma_buffer.data(),
+ chroma_buffer.size());
+ gpu.Maxwell3D().OnMemoryWrite();
+ break;
+ }
+ default:
+ UNIMPLEMENTED_MSG("Unknown video pixel format {}", config.pixel_format.Value());
+ break;
+ }
+}
+
+} // namespace Tegra
diff --git a/src/video_core/command_classes/vic.h b/src/video_core/command_classes/vic.h
new file mode 100644
index 000000000..8c4e284a1
--- /dev/null
+++ b/src/video_core/command_classes/vic.h
@@ -0,0 +1,110 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <vector>
+#include "common/bit_field.h"
+#include "common/common_types.h"
+
+struct SwsContext;
+
+namespace Tegra {
+class GPU;
+class Nvdec;
+
+struct PlaneOffsets {
+ u32 luma_offset{};
+ u32 chroma_u_offset{};
+ u32 chroma_v_offset{};
+};
+
+struct VicRegisters {
+ INSERT_PADDING_WORDS(64);
+ u32 nop{};
+ INSERT_PADDING_WORDS(15);
+ u32 pm_trigger{};
+ INSERT_PADDING_WORDS(47);
+ u32 set_application_id{};
+ u32 set_watchdog_timer{};
+ INSERT_PADDING_WORDS(17);
+ u32 context_save_area{};
+ u32 context_switch{};
+ INSERT_PADDING_WORDS(43);
+ u32 execute{};
+ INSERT_PADDING_WORDS(63);
+ std::array<std::array<PlaneOffsets, 8>, 8> surfacex_slots{};
+ u32 picture_index{};
+ u32 control_params{};
+ u32 config_struct_offset{};
+ u32 filter_struct_offset{};
+ u32 palette_offset{};
+ u32 hist_offset{};
+ u32 context_id{};
+ u32 fce_ucode_size{};
+ PlaneOffsets output_surface{};
+ u32 fce_ucode_offset{};
+ INSERT_PADDING_WORDS(4);
+ std::array<u32, 8> slot_context_id{};
+ INSERT_PADDING_WORDS(16);
+};
+static_assert(sizeof(VicRegisters) == 0x7A0, "VicRegisters is an invalid size");
+
+class Vic {
+public:
+ enum class Method : u32 {
+ Execute = 0xc0,
+ SetControlParams = 0x1c1,
+ SetConfigStructOffset = 0x1c2,
+ SetOutputSurfaceLumaOffset = 0x1c8,
+ SetOutputSurfaceChromaUOffset = 0x1c9,
+ SetOutputSurfaceChromaVOffset = 0x1ca
+ };
+
+ explicit Vic(GPU& gpu, std::shared_ptr<Nvdec> nvdec_processor);
+ ~Vic();
+
+ /// Write to the device state.
+ void ProcessMethod(Method method, const std::vector<u32>& arguments);
+
+private:
+ void Execute();
+
+ void VicStateWrite(u32 offset, u32 arguments);
+ VicRegisters vic_state{};
+
+ enum class VideoPixelFormat : u64_le {
+ RGBA8 = 0x1f,
+ BGRA8 = 0x20,
+ Yuv420 = 0x44,
+ };
+
+ union VicConfig {
+ u64_le raw{};
+ BitField<0, 7, u64_le> pixel_format;
+ BitField<7, 2, u64_le> chroma_loc_horiz;
+ BitField<9, 2, u64_le> chroma_loc_vert;
+ BitField<11, 4, u64_le> block_linear_kind;
+ BitField<15, 4, u64_le> block_linear_height_log2;
+ BitField<19, 3, u64_le> reserved0;
+ BitField<22, 10, u64_le> reserved1;
+ BitField<32, 14, u64_le> surface_width_minus1;
+ BitField<46, 14, u64_le> surface_height_minus1;
+ };
+
+ GPU& gpu;
+ std::shared_ptr<Tegra::Nvdec> nvdec_processor;
+
+ GPUVAddr config_struct_address{};
+ GPUVAddr output_surface_luma_address{};
+ GPUVAddr output_surface_chroma_u_address{};
+ GPUVAddr output_surface_chroma_v_address{};
+
+ SwsContext* scaler_ctx{};
+ s32 scaler_width{};
+ s32 scaler_height{};
+};
+
+} // namespace Tegra
diff --git a/src/video_core/dma_pusher.cpp b/src/video_core/dma_pusher.cpp
index f2f96ac33..d8801b1f5 100644
--- a/src/video_core/dma_pusher.cpp
+++ b/src/video_core/dma_pusher.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include "common/cityhash.h"
#include "common/microprofile.h"
#include "core/core.h"
#include "core/memory.h"
@@ -45,32 +46,41 @@ bool DmaPusher::Step() {
return false;
}
- const CommandList& command_list{dma_pushbuffer.front()};
- ASSERT_OR_EXECUTE(!command_list.empty(), {
- // Somehow the command_list is empty, in order to avoid a crash
- // We ignore it and assume its size is 0.
- dma_pushbuffer.pop();
- dma_pushbuffer_subindex = 0;
- return true;
- });
- const CommandListHeader command_list_header{command_list[dma_pushbuffer_subindex++]};
- const GPUVAddr dma_get = command_list_header.addr;
-
- if (dma_pushbuffer_subindex >= command_list.size()) {
- // We've gone through the current list, remove it from the queue
- dma_pushbuffer.pop();
- dma_pushbuffer_subindex = 0;
- }
+ CommandList& command_list{dma_pushbuffer.front()};
- if (command_list_header.size == 0) {
- return true;
- }
+ ASSERT_OR_EXECUTE(
+ command_list.command_lists.size() || command_list.prefetch_command_list.size(), {
+ // Somehow the command_list is empty, in order to avoid a crash
+ // We ignore it and assume its size is 0.
+ dma_pushbuffer.pop();
+ dma_pushbuffer_subindex = 0;
+ return true;
+ });
+
+ if (command_list.prefetch_command_list.size()) {
+ // Prefetched command list from nvdrv, used for things like synchronization
+ command_headers = std::move(command_list.prefetch_command_list);
+ dma_pushbuffer.pop();
+ } else {
+ const CommandListHeader command_list_header{
+ command_list.command_lists[dma_pushbuffer_subindex++]};
+ const GPUVAddr dma_get = command_list_header.addr;
+
+ if (dma_pushbuffer_subindex >= command_list.command_lists.size()) {
+ // We've gone through the current list, remove it from the queue
+ dma_pushbuffer.pop();
+ dma_pushbuffer_subindex = 0;
+ }
- // Push buffer non-empty, read a word
- command_headers.resize(command_list_header.size);
- gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
- command_list_header.size * sizeof(u32));
+ if (command_list_header.size == 0) {
+ return true;
+ }
+ // Push buffer non-empty, read a word
+ command_headers.resize(command_list_header.size);
+ gpu.MemoryManager().ReadBlockUnsafe(dma_get, command_headers.data(),
+ command_list_header.size * sizeof(u32));
+ }
for (std::size_t index = 0; index < command_headers.size();) {
const CommandHeader& command_header = command_headers[index];
diff --git a/src/video_core/dma_pusher.h b/src/video_core/dma_pusher.h
index efa90d170..96ac267f7 100644
--- a/src/video_core/dma_pusher.h
+++ b/src/video_core/dma_pusher.h
@@ -18,6 +18,8 @@ class System;
namespace Tegra {
+class GPU;
+
enum class SubmissionMode : u32 {
IncreasingOld = 0,
Increasing = 1,
@@ -27,6 +29,31 @@ enum class SubmissionMode : u32 {
IncreaseOnce = 5
};
+// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
+// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
+// So the values you see in docs might be multiplied by 4.
+enum class BufferMethods : u32 {
+ BindObject = 0x0,
+ Nop = 0x2,
+ SemaphoreAddressHigh = 0x4,
+ SemaphoreAddressLow = 0x5,
+ SemaphoreSequence = 0x6,
+ SemaphoreTrigger = 0x7,
+ NotifyIntr = 0x8,
+ WrcacheFlush = 0x9,
+ Unk28 = 0xA,
+ UnkCacheFlush = 0xB,
+ RefCnt = 0x14,
+ SemaphoreAcquire = 0x1A,
+ SemaphoreRelease = 0x1B,
+ FenceValue = 0x1C,
+ FenceAction = 0x1D,
+ WaitForInterrupt = 0x1E,
+ Unk7c = 0x1F,
+ Yield = 0x20,
+ NonPullerMethods = 0x40,
+};
+
struct CommandListHeader {
union {
u64 raw;
@@ -49,9 +76,23 @@ union CommandHeader {
static_assert(std::is_standard_layout_v<CommandHeader>, "CommandHeader is not standard layout");
static_assert(sizeof(CommandHeader) == sizeof(u32), "CommandHeader has incorrect size!");
-class GPU;
+inline CommandHeader BuildCommandHeader(BufferMethods method, u32 arg_count, SubmissionMode mode) {
+ CommandHeader result{};
+ result.method.Assign(static_cast<u32>(method));
+ result.arg_count.Assign(arg_count);
+ result.mode.Assign(mode);
+ return result;
+}
-using CommandList = std::vector<Tegra::CommandListHeader>;
+struct CommandList final {
+ CommandList() = default;
+ explicit CommandList(std::size_t size) : command_lists(size) {}
+ explicit CommandList(std::vector<Tegra::CommandHeader>&& prefetch_command_list)
+ : prefetch_command_list{std::move(prefetch_command_list)} {}
+
+ std::vector<Tegra::CommandListHeader> command_lists;
+ std::vector<Tegra::CommandHeader> prefetch_command_list;
+};
/**
* The DmaPusher class implements DMA submission to FIFOs, providing an area of memory that the
@@ -60,7 +101,7 @@ using CommandList = std::vector<Tegra::CommandListHeader>;
* See https://envytools.readthedocs.io/en/latest/hw/fifo/dma-pusher.html#fifo-dma-pusher for
* details on this implementation.
*/
-class DmaPusher {
+class DmaPusher final {
public:
explicit DmaPusher(Core::System& system, GPU& gpu);
~DmaPusher();
diff --git a/src/video_core/engines/fermi_2d.cpp b/src/video_core/engines/fermi_2d.cpp
index 6e50661a3..9409c4075 100644
--- a/src/video_core/engines/fermi_2d.cpp
+++ b/src/video_core/engines/fermi_2d.cpp
@@ -87,12 +87,12 @@ void Fermi2D::HandleSurfaceCopy() {
const Common::Rectangle<u32> src_rect{src_blit_x1, src_blit_y1, src_blit_x2, src_blit_y2};
const Common::Rectangle<u32> dst_rect{regs.blit_dst_x, regs.blit_dst_y, dst_blit_x2,
dst_blit_y2};
- Config copy_config;
- copy_config.operation = regs.operation;
- copy_config.filter = regs.blit_control.filter;
- copy_config.src_rect = src_rect;
- copy_config.dst_rect = dst_rect;
-
+ const Config copy_config{
+ .operation = regs.operation,
+ .filter = regs.blit_control.filter,
+ .src_rect = src_rect,
+ .dst_rect = dst_rect,
+ };
if (!rasterizer->AccelerateSurfaceCopy(regs.src, regs.dst, copy_config)) {
UNIMPLEMENTED();
}
diff --git a/src/video_core/engines/fermi_2d.h b/src/video_core/engines/fermi_2d.h
index 213abfaae..0909709ec 100644
--- a/src/video_core/engines/fermi_2d.h
+++ b/src/video_core/engines/fermi_2d.h
@@ -145,8 +145,8 @@ public:
} regs{};
struct Config {
- Operation operation;
- Filter filter;
+ Operation operation{};
+ Filter filter{};
Common::Rectangle<u32> src_rect;
Common::Rectangle<u32> dst_rect;
};
diff --git a/src/video_core/engines/maxwell_3d.cpp b/src/video_core/engines/maxwell_3d.cpp
index 33854445f..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() {
mme_inline[MAXWELL3D_REG_INDEX(index_array.count)] = true;
}
+void Maxwell3D::ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call) {
+ if (executing_macro == 0) {
+ // A macro call must begin by writing the macro method's register, not its argument.
+ ASSERT_MSG((method % 2) == 0,
+ "Can't start macro execution by writing to the ARGS register");
+ executing_macro = method;
+ }
+
+ macro_params.insert(macro_params.end(), base_start, base_start + amount);
+
+ // Call the macro when there are no more parameters in the command buffer
+ if (is_last_call) {
+ CallMacroMethod(executing_macro, macro_params);
+ macro_params.clear();
+ }
+}
+
+u32 Maxwell3D::ProcessShadowRam(u32 method, u32 argument) {
+ // Keep track of the register value in shadow_state when requested.
+ const auto control = shadow_state.shadow_ram_control;
+ if (control == Regs::ShadowRamControl::Track ||
+ control == Regs::ShadowRamControl::TrackWithFilter) {
+ shadow_state.reg_array[method] = argument;
+ return argument;
+ }
+ if (control == Regs::ShadowRamControl::Replay) {
+ return shadow_state.reg_array[method];
+ }
+ return argument;
+}
+
+void Maxwell3D::ProcessDirtyRegisters(u32 method, u32 argument) {
+ if (regs.reg_array[method] == argument) {
+ return;
+ }
+ regs.reg_array[method] = argument;
+
+ for (const auto& table : dirty.tables) {
+ dirty.flags[table[method]] = true;
+ }
+}
+
+void Maxwell3D::ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument,
+ bool is_last_call) {
+ switch (method) {
+ case MAXWELL3D_REG_INDEX(wait_for_idle):
+ return rasterizer->WaitForIdle();
+ case MAXWELL3D_REG_INDEX(shadow_ram_control):
+ shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(nonshadow_argument);
+ return;
+ case MAXWELL3D_REG_INDEX(macros.data):
+ return macro_engine->AddCode(regs.macros.upload_address, argument);
+ case MAXWELL3D_REG_INDEX(macros.bind):
+ return ProcessMacroBind(argument);
+ case MAXWELL3D_REG_INDEX(firmware[4]):
+ return ProcessFirmwareCall4();
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]):
+ return StartCBData(method);
+ case MAXWELL3D_REG_INDEX(cb_bind[0]):
+ return ProcessCBBind(0);
+ case MAXWELL3D_REG_INDEX(cb_bind[1]):
+ return ProcessCBBind(1);
+ case MAXWELL3D_REG_INDEX(cb_bind[2]):
+ return ProcessCBBind(2);
+ case MAXWELL3D_REG_INDEX(cb_bind[3]):
+ return ProcessCBBind(3);
+ case MAXWELL3D_REG_INDEX(cb_bind[4]):
+ return ProcessCBBind(4);
+ case MAXWELL3D_REG_INDEX(draw.vertex_end_gl):
+ return DrawArrays();
+ case MAXWELL3D_REG_INDEX(clear_buffers):
+ return ProcessClearBuffers();
+ case MAXWELL3D_REG_INDEX(query.query_get):
+ return ProcessQueryGet();
+ case MAXWELL3D_REG_INDEX(condition.mode):
+ return ProcessQueryCondition();
+ case MAXWELL3D_REG_INDEX(counter_reset):
+ return ProcessCounterReset();
+ case MAXWELL3D_REG_INDEX(sync_info):
+ return ProcessSyncPoint();
+ case MAXWELL3D_REG_INDEX(exec_upload):
+ return upload_state.ProcessExec(regs.exec_upload.linear != 0);
+ case MAXWELL3D_REG_INDEX(data_upload):
+ upload_state.ProcessData(argument, is_last_call);
+ if (is_last_call) {
+ OnMemoryWrite();
+ }
+ return;
+ }
+}
+
void Maxwell3D::CallMacroMethod(u32 method, const std::vector<u32>& parameters) {
// Reset the current macro.
executing_macro = 0;
@@ -157,142 +263,16 @@ void Maxwell3D::CallMethod(u32 method, u32 method_argument, bool is_last_call) {
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
// uploaded to the GPU during initialization.
if (method >= MacroRegistersStart) {
- // We're trying to execute a macro
- if (executing_macro == 0) {
- // A macro call must begin by writing the macro method's register, not its argument.
- ASSERT_MSG((method % 2) == 0,
- "Can't start macro execution by writing to the ARGS register");
- executing_macro = method;
- }
-
- macro_params.push_back(method_argument);
-
- // Call the macro when there are no more parameters in the command buffer
- if (is_last_call) {
- CallMacroMethod(executing_macro, macro_params);
- macro_params.clear();
- }
+ ProcessMacro(method, &method_argument, 1, is_last_call);
return;
}
ASSERT_MSG(method < Regs::NUM_REGS,
"Invalid Maxwell3D register, increase the size of the Regs structure");
- u32 arg = method_argument;
- // Keep track of the register value in shadow_state when requested.
- if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Track ||
- shadow_state.shadow_ram_control == Regs::ShadowRamControl::TrackWithFilter) {
- shadow_state.reg_array[method] = arg;
- } else if (shadow_state.shadow_ram_control == Regs::ShadowRamControl::Replay) {
- arg = shadow_state.reg_array[method];
- }
-
- if (regs.reg_array[method] != arg) {
- regs.reg_array[method] = arg;
-
- for (const auto& table : dirty.tables) {
- dirty.flags[table[method]] = true;
- }
- }
-
- switch (method) {
- case MAXWELL3D_REG_INDEX(wait_for_idle): {
- rasterizer->WaitForIdle();
- break;
- }
- case MAXWELL3D_REG_INDEX(shadow_ram_control): {
- shadow_state.shadow_ram_control = static_cast<Regs::ShadowRamControl>(method_argument);
- break;
- }
- case MAXWELL3D_REG_INDEX(macros.data): {
- macro_engine->AddCode(regs.macros.upload_address, arg);
- break;
- }
- case MAXWELL3D_REG_INDEX(macros.bind): {
- ProcessMacroBind(arg);
- break;
- }
- case MAXWELL3D_REG_INDEX(firmware[4]): {
- ProcessFirmwareCall4();
- break;
- }
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[0]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[1]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[2]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[3]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[4]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[5]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[6]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[7]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[8]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[9]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[10]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[11]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
- StartCBData(method);
- break;
- }
- case MAXWELL3D_REG_INDEX(cb_bind[0]): {
- ProcessCBBind(0);
- break;
- }
- case MAXWELL3D_REG_INDEX(cb_bind[1]): {
- ProcessCBBind(1);
- break;
- }
- case MAXWELL3D_REG_INDEX(cb_bind[2]): {
- ProcessCBBind(2);
- break;
- }
- case MAXWELL3D_REG_INDEX(cb_bind[3]): {
- ProcessCBBind(3);
- break;
- }
- case MAXWELL3D_REG_INDEX(cb_bind[4]): {
- ProcessCBBind(4);
- break;
- }
- case MAXWELL3D_REG_INDEX(draw.vertex_end_gl): {
- DrawArrays();
- break;
- }
- case MAXWELL3D_REG_INDEX(clear_buffers): {
- ProcessClearBuffers();
- break;
- }
- case MAXWELL3D_REG_INDEX(query.query_get): {
- ProcessQueryGet();
- break;
- }
- case MAXWELL3D_REG_INDEX(condition.mode): {
- ProcessQueryCondition();
- break;
- }
- case MAXWELL3D_REG_INDEX(counter_reset): {
- ProcessCounterReset();
- break;
- }
- case MAXWELL3D_REG_INDEX(sync_info): {
- ProcessSyncPoint();
- break;
- }
- case MAXWELL3D_REG_INDEX(exec_upload): {
- upload_state.ProcessExec(regs.exec_upload.linear != 0);
- break;
- }
- case MAXWELL3D_REG_INDEX(data_upload): {
- upload_state.ProcessData(arg, is_last_call);
- if (is_last_call) {
- OnMemoryWrite();
- }
- break;
- }
- default:
- break;
- }
+ const u32 argument = ProcessShadowRam(method, method_argument);
+ ProcessDirtyRegisters(method, argument);
+ ProcessMethodCall(method, argument, method_argument, is_last_call);
}
void 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,
// Methods after 0xE00 are special, they're actually triggers for some microcode that was
// uploaded to the GPU during initialization.
if (method >= MacroRegistersStart) {
- // We're trying to execute a macro
- if (executing_macro == 0) {
- // A macro call must begin by writing the macro method's register, not its argument.
- ASSERT_MSG((method % 2) == 0,
- "Can't start macro execution by writing to the ARGS register");
- executing_macro = method;
- }
-
- for (std::size_t i = 0; i < amount; i++) {
- macro_params.push_back(base_start[i]);
- }
-
- // Call the macro when there are no more parameters in the command buffer
- if (amount == methods_pending) {
- CallMacroMethod(executing_macro, macro_params);
- macro_params.clear();
- }
+ ProcessMacro(method, base_start, amount, amount == methods_pending);
return;
}
switch (method) {
@@ -335,15 +299,14 @@ void Maxwell3D::CallMultiMethod(u32 method, const u32* base_start, u32 amount,
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[12]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[13]):
case MAXWELL3D_REG_INDEX(const_buffer.cb_data[14]):
- case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]): {
+ case MAXWELL3D_REG_INDEX(const_buffer.cb_data[15]):
ProcessCBMultiData(method, base_start, amount);
break;
- }
- default: {
+ default:
for (std::size_t i = 0; i < amount; i++) {
CallMethod(method, base_start[i], methods_pending - static_cast<u32>(i) <= 1);
}
- }
+ break;
}
}
@@ -597,7 +560,7 @@ std::optional<u64> Maxwell3D::GetQueryResult() {
// Deferred.
rasterizer->Query(regs.query.QueryAddress(), VideoCore::QueryType::SamplesPassed,
system.GPU().GetTicks());
- return {};
+ return std::nullopt;
default:
LOG_DEBUG(HW_GPU, "Unimplemented query select type {}",
static_cast<u32>(regs.query.query_get.select.Value()));
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:
private:
void InitializeRegisterDefaults();
+ void ProcessMacro(u32 method, const u32* base_start, u32 amount, bool is_last_call);
+
+ u32 ProcessShadowRam(u32 method, u32 argument);
+
+ void ProcessDirtyRegisters(u32 method, u32 argument);
+
+ void ProcessMethodCall(u32 method, u32 argument, u32 nonshadow_argument, bool is_last_call);
+
Core::System& system;
MemoryManager& memory_manager;
diff --git a/src/video_core/engines/maxwell_dma.cpp b/src/video_core/engines/maxwell_dma.cpp
index e88290754..8fa359d0a 100644
--- a/src/video_core/engines/maxwell_dma.cpp
+++ b/src/video_core/engines/maxwell_dma.cpp
@@ -114,8 +114,6 @@ void MaxwellDMA::CopyBlockLinearToPitch() {
const u32 block_depth = src_params.block_size.depth;
const size_t src_size =
CalculateSize(true, bytes_per_pixel, width, height, depth, block_height, block_depth);
- const size_t src_layer_size =
- CalculateSize(true, bytes_per_pixel, width, height, 1, block_height, block_depth);
if (read_buffer.size() < src_size) {
read_buffer.resize(src_size);
diff --git a/src/video_core/engines/shader_bytecode.h b/src/video_core/engines/shader_bytecode.h
index d374b73cf..37d17efdc 100644
--- a/src/video_core/engines/shader_bytecode.h
+++ b/src/video_core/engines/shader_bytecode.h
@@ -32,31 +32,31 @@ struct Register {
constexpr Register() = default;
- constexpr Register(u64 value) : value(value) {}
+ constexpr Register(u64 value_) : value(value_) {}
- constexpr operator u64() const {
+ [[nodiscard]] constexpr operator u64() const {
return value;
}
template <typename T>
- constexpr u64 operator-(const T& oth) const {
+ [[nodiscard]] constexpr u64 operator-(const T& oth) const {
return value - oth;
}
template <typename T>
- constexpr u64 operator&(const T& oth) const {
+ [[nodiscard]] constexpr u64 operator&(const T& oth) const {
return value & oth;
}
- constexpr u64 operator&(const Register& oth) const {
+ [[nodiscard]] constexpr u64 operator&(const Register& oth) const {
return value & oth.value;
}
- constexpr u64 operator~() const {
+ [[nodiscard]] constexpr u64 operator~() const {
return ~value;
}
- u64 GetSwizzledIndex(u64 elem) const {
+ [[nodiscard]] u64 GetSwizzledIndex(u64 elem) const {
elem = (value + elem) & 3;
return (value & ~3) + elem;
}
@@ -75,7 +75,7 @@ enum class AttributeSize : u64 {
union Attribute {
Attribute() = default;
- constexpr explicit Attribute(u64 value) : value(value) {}
+ constexpr explicit Attribute(u64 value_) : value(value_) {}
enum class Index : u64 {
LayerViewportPointSize = 6,
@@ -107,7 +107,7 @@ union Attribute {
BitField<31, 1, u64> patch;
BitField<47, 3, AttributeSize> size;
- bool IsPhysical() const {
+ [[nodiscard]] bool IsPhysical() const {
return patch == 0 && element == 0 && static_cast<u64>(index.Value()) == 0;
}
} fmt20;
@@ -124,7 +124,7 @@ union Attribute {
union Sampler {
Sampler() = default;
- constexpr explicit Sampler(u64 value) : value(value) {}
+ constexpr explicit Sampler(u64 value_) : value(value_) {}
enum class Index : u64 {
Sampler_0 = 8,
@@ -137,7 +137,7 @@ union Sampler {
union Image {
Image() = default;
- constexpr explicit Image(u64 value) : value{value} {}
+ constexpr explicit Image(u64 value_) : value{value_} {}
BitField<36, 13, u64> index;
u64 value;
@@ -505,14 +505,14 @@ struct IpaMode {
IpaInterpMode interpolation_mode;
IpaSampleMode sampling_mode;
- bool operator==(const IpaMode& a) const {
+ [[nodiscard]] bool operator==(const IpaMode& a) const {
return std::tie(interpolation_mode, sampling_mode) ==
std::tie(a.interpolation_mode, a.sampling_mode);
}
- bool operator!=(const IpaMode& a) const {
+ [[nodiscard]] bool operator!=(const IpaMode& a) const {
return !operator==(a);
}
- bool operator<(const IpaMode& a) const {
+ [[nodiscard]] bool operator<(const IpaMode& a) const {
return std::tie(interpolation_mode, sampling_mode) <
std::tie(a.interpolation_mode, a.sampling_mode);
}
@@ -658,10 +658,10 @@ union Instruction {
return *this;
}
- constexpr Instruction(u64 value) : value{value} {}
+ constexpr Instruction(u64 value_) : value{value_} {}
constexpr Instruction(const Instruction& instr) : value(instr.value) {}
- constexpr bool Bit(u64 offset) const {
+ [[nodiscard]] constexpr bool Bit(u64 offset) const {
return ((value >> offset) & 1) != 0;
}
@@ -746,34 +746,34 @@ union Instruction {
BitField<28, 8, u64> imm_lut28;
BitField<48, 8, u64> imm_lut48;
- u32 GetImmLut28() const {
+ [[nodiscard]] u32 GetImmLut28() const {
return static_cast<u32>(imm_lut28);
}
- u32 GetImmLut48() const {
+ [[nodiscard]] u32 GetImmLut48() const {
return static_cast<u32>(imm_lut48);
}
} lop3;
- u16 GetImm20_16() const {
+ [[nodiscard]] u16 GetImm20_16() const {
return static_cast<u16>(imm20_16);
}
- u32 GetImm20_19() const {
+ [[nodiscard]] u32 GetImm20_19() const {
u32 imm{static_cast<u32>(imm20_19)};
imm <<= 12;
imm |= negate_imm ? 0x80000000 : 0;
return imm;
}
- u32 GetImm20_32() const {
+ [[nodiscard]] u32 GetImm20_32() const {
return static_cast<u32>(imm20_32);
}
- s32 GetSignedImm20_20() const {
- u32 immediate = static_cast<u32>(imm20_19 | (negate_imm << 19));
+ [[nodiscard]] s32 GetSignedImm20_20() const {
+ const auto immediate = static_cast<u32>(imm20_19 | (negate_imm << 19));
// Sign extend the 20-bit value.
- u32 mask = 1U << (20 - 1);
+ const auto mask = 1U << (20 - 1);
return static_cast<s32>((immediate ^ mask) - mask);
}
} alu;
@@ -857,7 +857,7 @@ union Instruction {
BitField<56, 1, u64> second_negate;
BitField<30, 9, u64> second;
- u32 PackImmediates() const {
+ [[nodiscard]] u32 PackImmediates() const {
// Immediates are half floats shifted.
constexpr u32 imm_shift = 6;
return static_cast<u32>((first << imm_shift) | (second << (16 + imm_shift)));
@@ -1033,7 +1033,7 @@ union Instruction {
BitField<28, 2, AtomicType> type;
BitField<30, 22, s64> offset;
- s32 GetImmediateOffset() const {
+ [[nodiscard]] s32 GetImmediateOffset() const {
return static_cast<s32>(offset << 2);
}
} atoms;
@@ -1215,7 +1215,7 @@ union Instruction {
BitField<39, 4, u64> rounding;
// H0, H1 extract for F16 missing
BitField<41, 1, u64> selector; // Guessed as some games set it, TODO: reverse this value
- F2fRoundingOp GetRoundingMode() const {
+ [[nodiscard]] F2fRoundingOp GetRoundingMode() const {
constexpr u64 rounding_mask = 0x0B;
return static_cast<F2fRoundingOp>(rounding.Value() & rounding_mask);
}
@@ -1239,15 +1239,15 @@ union Instruction {
BitField<54, 1, u64> aoffi_flag;
BitField<55, 3, TextureProcessMode> process_mode;
- bool IsComponentEnabled(std::size_t component) const {
- return ((1ull << component) & component_mask) != 0;
+ [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
+ return ((1ULL << component) & component_mask) != 0;
}
- TextureProcessMode GetTextureProcessMode() const {
+ [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
return process_mode;
}
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
@@ -1271,15 +1271,15 @@ union Instruction {
BitField<36, 1, u64> aoffi_flag;
BitField<37, 3, TextureProcessMode> process_mode;
- bool IsComponentEnabled(std::size_t component) const {
+ [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
return ((1ULL << component) & component_mask) != 0;
}
- TextureProcessMode GetTextureProcessMode() const {
+ [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
return process_mode;
}
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
@@ -1299,7 +1299,7 @@ union Instruction {
BitField<31, 4, u64> component_mask;
BitField<49, 1, u64> nodep_flag;
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NODEP:
return nodep_flag != 0;
@@ -1309,7 +1309,7 @@ union Instruction {
return false;
}
- bool IsComponentEnabled(std::size_t component) const {
+ [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
return ((1ULL << component) & component_mask) != 0;
}
} txq;
@@ -1321,11 +1321,11 @@ union Instruction {
BitField<35, 1, u64> ndv_flag;
BitField<49, 1, u64> nodep_flag;
- bool IsComponentEnabled(std::size_t component) const {
- return ((1ull << component) & component_mask) != 0;
+ [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
+ return ((1ULL << component) & component_mask) != 0;
}
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return (ndv_flag != 0);
@@ -1347,7 +1347,7 @@ union Instruction {
BitField<54, 2, u64> offset_mode;
BitField<56, 2, u64> component;
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return ndv_flag != 0;
@@ -1373,7 +1373,7 @@ union Instruction {
BitField<33, 2, u64> offset_mode;
BitField<37, 2, u64> component;
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::NDV:
return ndv_flag != 0;
@@ -1399,7 +1399,7 @@ union Instruction {
BitField<52, 2, u64> component;
BitField<55, 1, u64> fp16_flag;
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return dc_flag != 0;
@@ -1422,16 +1422,20 @@ union Instruction {
BitField<53, 4, u64> texture_info;
BitField<59, 1, u64> fp32_flag;
- TextureType GetTextureType() const {
+ [[nodiscard]] TextureType GetTextureType() const {
// The TEXS instruction has a weird encoding for the texture type.
- if (texture_info == 0)
+ if (texture_info == 0) {
return TextureType::Texture1D;
- if (texture_info >= 1 && texture_info <= 9)
+ }
+ if (texture_info >= 1 && texture_info <= 9) {
return TextureType::Texture2D;
- if (texture_info >= 10 && texture_info <= 11)
+ }
+ if (texture_info >= 10 && texture_info <= 11) {
return TextureType::Texture3D;
- if (texture_info >= 12 && texture_info <= 13)
+ }
+ if (texture_info >= 12 && texture_info <= 13) {
return TextureType::TextureCube;
+ }
LOG_CRITICAL(HW_GPU, "Unhandled texture_info: {}",
static_cast<u32>(texture_info.Value()));
@@ -1439,7 +1443,7 @@ union Instruction {
return TextureType::Texture1D;
}
- TextureProcessMode GetTextureProcessMode() const {
+ [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
switch (texture_info) {
case 0:
case 2:
@@ -1458,7 +1462,7 @@ union Instruction {
return TextureProcessMode::None;
}
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::DC:
return (texture_info >= 4 && texture_info <= 6) || texture_info == 9;
@@ -1470,16 +1474,16 @@ union Instruction {
return false;
}
- bool IsArrayTexture() const {
+ [[nodiscard]] bool IsArrayTexture() const {
// TEXS only supports Texture2D arrays.
return texture_info >= 7 && texture_info <= 9;
}
- bool HasTwoDestinations() const {
+ [[nodiscard]] bool HasTwoDestinations() const {
return gpr28.Value() != Register::ZeroIndex;
}
- bool IsComponentEnabled(std::size_t component) const {
+ [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
static constexpr std::array<std::array<u32, 8>, 4> mask_lut{{
{},
{0x1, 0x2, 0x4, 0x8, 0x3, 0x9, 0xa, 0xc},
@@ -1506,7 +1510,7 @@ union Instruction {
BitField<54, 1, u64> cl;
BitField<55, 1, u64> process_mode;
- TextureProcessMode GetTextureProcessMode() const {
+ [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
return process_mode == 0 ? TextureProcessMode::LZ : TextureProcessMode::LL;
}
} tld;
@@ -1516,7 +1520,7 @@ union Instruction {
BitField<53, 4, u64> texture_info;
BitField<59, 1, u64> fp32_flag;
- TextureType GetTextureType() const {
+ [[nodiscard]] TextureType GetTextureType() const {
// The TLDS instruction has a weird encoding for the texture type.
if (texture_info <= 1) {
return TextureType::Texture1D;
@@ -1535,13 +1539,14 @@ union Instruction {
return TextureType::Texture1D;
}
- TextureProcessMode GetTextureProcessMode() const {
- if (texture_info == 1 || texture_info == 5 || texture_info == 12)
+ [[nodiscard]] TextureProcessMode GetTextureProcessMode() const {
+ if (texture_info == 1 || texture_info == 5 || texture_info == 12) {
return TextureProcessMode::LL;
+ }
return TextureProcessMode::LZ;
}
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::AOFFI:
return texture_info == 12 || texture_info == 4;
@@ -1555,7 +1560,7 @@ union Instruction {
return false;
}
- bool IsArrayTexture() const {
+ [[nodiscard]] bool IsArrayTexture() const {
// TEXS only supports Texture2D arrays.
return texture_info == 8;
}
@@ -1567,7 +1572,7 @@ union Instruction {
BitField<35, 1, u64> aoffi_flag;
BitField<49, 1, u64> nodep_flag;
- bool UsesMiscMode(TextureMiscMode mode) const {
+ [[nodiscard]] bool UsesMiscMode(TextureMiscMode mode) const {
switch (mode) {
case TextureMiscMode::AOFFI:
return aoffi_flag != 0;
@@ -1591,7 +1596,7 @@ union Instruction {
BitField<20, 3, StoreType> store_data_layout;
BitField<20, 4, u64> component_mask_selector;
- bool IsComponentEnabled(std::size_t component) const {
+ [[nodiscard]] bool IsComponentEnabled(std::size_t component) const {
ASSERT(mode == SurfaceDataMode::P);
constexpr u8 R = 0b0001;
constexpr u8 G = 0b0010;
@@ -1604,7 +1609,7 @@ union Instruction {
return std::bitset<4>{mask.at(component_mask_selector)}.test(component);
}
- StoreType GetStoreDataLayout() const {
+ [[nodiscard]] StoreType GetStoreDataLayout() const {
ASSERT(mode == SurfaceDataMode::D_BA);
return store_data_layout;
}
@@ -1622,14 +1627,15 @@ union Instruction {
BitField<20, 24, u64> target;
BitField<5, 1, u64> constant_buffer;
- s32 GetBranchTarget() const {
+ [[nodiscard]] s32 GetBranchTarget() const {
// Sign extend the branch target offset
- u32 mask = 1U << (24 - 1);
- u32 value = static_cast<u32>(target);
+ const auto mask = 1U << (24 - 1);
+ const auto target_value = static_cast<u32>(target);
+ constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction));
+
// The branch offset is relative to the next instruction and is stored in bytes, so
// divide it by the size of an instruction and add 1 to it.
- return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) +
- 1;
+ return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1;
}
} bra;
@@ -1637,14 +1643,15 @@ union Instruction {
BitField<20, 24, u64> target;
BitField<5, 1, u64> constant_buffer;
- s32 GetBranchExtend() const {
+ [[nodiscard]] s32 GetBranchExtend() const {
// Sign extend the branch target offset
- u32 mask = 1U << (24 - 1);
- u32 value = static_cast<u32>(target);
+ const auto mask = 1U << (24 - 1);
+ const auto target_value = static_cast<u32>(target);
+ constexpr auto instruction_size = static_cast<s32>(sizeof(Instruction));
+
// The branch offset is relative to the next instruction and is stored in bytes, so
// divide it by the size of an instruction and add 1 to it.
- return static_cast<s32>((value ^ mask) - mask) / static_cast<s32>(sizeof(Instruction)) +
- 1;
+ return static_cast<s32>((target_value ^ mask) - mask) / instruction_size + 1;
}
} brx;
@@ -1697,7 +1704,7 @@ union Instruction {
BitField<50, 1, u64> is_op_b_register;
BitField<51, 3, VmnmxOperation> operation;
- VmnmxType SourceFormatA() const {
+ [[nodiscard]] VmnmxType SourceFormatA() const {
switch (src_format_a) {
case 0b11:
return VmnmxType::Bits32;
@@ -1708,7 +1715,7 @@ union Instruction {
}
}
- VmnmxType SourceFormatB() const {
+ [[nodiscard]] VmnmxType SourceFormatB() const {
switch (src_format_b) {
case 0b11:
return VmnmxType::Bits32;
@@ -1739,7 +1746,7 @@ union Instruction {
BitField<20, 14, u64> shifted_offset;
BitField<34, 5, u64> index;
- u64 GetOffset() const {
+ [[nodiscard]] u64 GetOffset() const {
return shifted_offset * 4;
}
} cbuf34;
@@ -1748,7 +1755,7 @@ union Instruction {
BitField<20, 16, s64> offset;
BitField<36, 5, u64> index;
- s64 GetOffset() const {
+ [[nodiscard]] s64 GetOffset() const {
return offset;
}
} cbuf36;
@@ -1893,6 +1900,7 @@ public:
ICMP_IMM,
FCMP_RR,
FCMP_RC,
+ FCMP_IMMR,
MUFU, // Multi-Function Operator
RRO_C, // Range Reduction Operator
RRO_R,
@@ -1996,29 +2004,29 @@ public:
/// Returns whether an opcode has an execution predicate field or not (ie, whether it can be
/// conditionally executed).
- static bool IsPredicatedInstruction(Id opcode) {
+ [[nodiscard]] static bool IsPredicatedInstruction(Id opcode) {
// TODO(Subv): Add the rest of unpredicated instructions.
return opcode != Id::SSY && opcode != Id::PBK;
}
class Matcher {
public:
- constexpr Matcher(const char* const name, u16 mask, u16 expected, Id id, Type type)
- : name{name}, mask{mask}, expected{expected}, id{id}, type{type} {}
+ constexpr Matcher(const char* const name_, u16 mask_, u16 expected_, Id id_, Type type_)
+ : name{name_}, mask{mask_}, expected{expected_}, id{id_}, type{type_} {}
- constexpr const char* GetName() const {
+ [[nodiscard]] constexpr const char* GetName() const {
return name;
}
- constexpr u16 GetMask() const {
+ [[nodiscard]] constexpr u16 GetMask() const {
return mask;
}
- constexpr Id GetId() const {
+ [[nodiscard]] constexpr Id GetId() const {
return id;
}
- constexpr Type GetType() const {
+ [[nodiscard]] constexpr Type GetType() const {
return type;
}
@@ -2027,7 +2035,7 @@ public:
* @param instruction The instruction to test
* @returns true if the given instruction matches.
*/
- constexpr bool Matches(u16 instruction) const {
+ [[nodiscard]] constexpr bool Matches(u16 instruction) const {
return (instruction & mask) == expected;
}
@@ -2039,7 +2047,8 @@ public:
Type type;
};
- static std::optional<std::reference_wrapper<const Matcher>> Decode(Instruction instr) {
+ using DecodeResult = std::optional<std::reference_wrapper<const Matcher>>;
+ [[nodiscard]] static DecodeResult Decode(Instruction instr) {
static const auto table{GetDecodeTable()};
const auto matches_instruction = [instr](const auto& matcher) {
@@ -2061,7 +2070,7 @@ private:
* A '0' in a bitstring indicates that a zero must be present at that bit position.
* A '1' in a bitstring indicates that a one must be present at that bit position.
*/
- static constexpr auto GetMaskAndExpect(const char* const bitstring) {
+ [[nodiscard]] static constexpr auto GetMaskAndExpect(const char* const bitstring) {
u16 mask = 0, expect = 0;
for (std::size_t i = 0; i < opcode_bitsize; i++) {
const std::size_t bit_position = opcode_bitsize - i - 1;
@@ -2083,14 +2092,14 @@ private:
public:
/// Creates a matcher that can match and parse instructions based on bitstring.
- static constexpr auto GetMatcher(const char* const bitstring, Id op, Type type,
- const char* const name) {
+ [[nodiscard]] static constexpr auto GetMatcher(const char* const bitstring, Id op,
+ Type type, const char* const name) {
const auto [mask, expected] = GetMaskAndExpect(bitstring);
return Matcher(name, mask, expected, op, type);
}
};
- static std::vector<Matcher> GetDecodeTable() {
+ [[nodiscard]] static std::vector<Matcher> GetDecodeTable() {
std::vector<Matcher> table = {
#define INST(bitstring, op, type, name) Detail::GetMatcher(bitstring, op, type, name)
INST("111000110011----", Id::KIL, Type::Flow, "KIL"),
@@ -2205,6 +2214,7 @@ private:
INST("0111110-0-------", Id::HSET2_IMM, Type::HalfSet, "HSET2_IMM"),
INST("010110111010----", Id::FCMP_RR, Type::Arithmetic, "FCMP_RR"),
INST("010010111010----", Id::FCMP_RC, Type::Arithmetic, "FCMP_RC"),
+ INST("0011011-1010----", Id::FCMP_IMMR, Type::Arithmetic, "FCMP_IMMR"),
INST("0101000010000---", Id::MUFU, Type::Arithmetic, "MUFU"),
INST("0100110010010---", Id::RRO_C, Type::Arithmetic, "RRO_C"),
INST("0101110010010---", Id::RRO_R, Type::Arithmetic, "RRO_R"),
diff --git a/src/video_core/engines/shader_header.h b/src/video_core/engines/shader_header.h
index 72e2a33d5..ceec05459 100644
--- a/src/video_core/engines/shader_header.h
+++ b/src/video_core/engines/shader_header.h
@@ -41,30 +41,30 @@ struct Header {
BitField<26, 1, u32> does_load_or_store;
BitField<27, 1, u32> does_fp64;
BitField<28, 4, u32> stream_out_mask;
- } common0{};
+ } common0;
union {
BitField<0, 24, u32> shader_local_memory_low_size;
BitField<24, 8, u32> per_patch_attribute_count;
- } common1{};
+ } common1;
union {
BitField<0, 24, u32> shader_local_memory_high_size;
BitField<24, 8, u32> threads_per_input_primitive;
- } common2{};
+ } common2;
union {
BitField<0, 24, u32> shader_local_memory_crs_size;
BitField<24, 4, OutputTopology> output_topology;
BitField<28, 4, u32> reserved;
- } common3{};
+ } common3;
union {
BitField<0, 12, u32> max_output_vertices;
BitField<12, 8, u32> store_req_start; // NOTE: not used by geometry shaders.
BitField<20, 4, u32> reserved;
BitField<24, 8, u32> store_req_end; // NOTE: not used by geometry shaders.
- } common4{};
+ } common4;
union {
struct {
@@ -145,7 +145,7 @@ struct Header {
}
} ps;
- std::array<u32, 0xF> raw{};
+ std::array<u32, 0xF> raw;
};
u64 GetLocalMemorySize() const {
@@ -153,7 +153,6 @@ struct Header {
(common2.shader_local_memory_high_size << 24));
}
};
-
static_assert(sizeof(Header) == 0x50, "Incorrect structure size");
} // namespace Tegra::Shader
diff --git a/src/video_core/fence_manager.h b/src/video_core/fence_manager.h
index 06cc12d5a..de6991ef6 100644
--- a/src/video_core/fence_manager.h
+++ b/src/video_core/fence_manager.h
@@ -74,8 +74,6 @@ public:
}
void WaitPendingFences() {
- auto& gpu{system.GPU()};
- auto& memory_manager{gpu.MemoryManager()};
while (!fences.empty()) {
TFence& current_fence = fences.front();
if (ShouldWait()) {
@@ -83,8 +81,8 @@ public:
}
PopAsyncFlushes();
if (current_fence->IsSemaphore()) {
- memory_manager.template Write<u32>(current_fence->GetAddress(),
- current_fence->GetPayload());
+ gpu_memory.template Write<u32>(current_fence->GetAddress(),
+ current_fence->GetPayload());
} else {
gpu.IncrementSyncPoint(current_fence->GetPayload());
}
@@ -93,13 +91,13 @@ public:
}
protected:
- FenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- TTextureCache& texture_cache, TTBufferCache& buffer_cache,
- TQueryCache& query_cache)
- : system{system}, rasterizer{rasterizer}, texture_cache{texture_cache},
- buffer_cache{buffer_cache}, query_cache{query_cache} {}
+ explicit FenceManager(VideoCore::RasterizerInterface& rasterizer_, Tegra::GPU& gpu_,
+ TTextureCache& texture_cache_, TTBufferCache& buffer_cache_,
+ TQueryCache& query_cache_)
+ : rasterizer{rasterizer_}, gpu{gpu_}, gpu_memory{gpu.MemoryManager()},
+ texture_cache{texture_cache_}, buffer_cache{buffer_cache_}, query_cache{query_cache_} {}
- virtual ~FenceManager() {}
+ virtual ~FenceManager() = default;
/// Creates a Sync Point Fence Interface, does not create a backend fence if 'is_stubbed' is
/// true
@@ -113,16 +111,15 @@ protected:
/// Waits until a fence has been signalled by the host GPU.
virtual void WaitFence(TFence& fence) = 0;
- Core::System& system;
VideoCore::RasterizerInterface& rasterizer;
+ Tegra::GPU& gpu;
+ Tegra::MemoryManager& gpu_memory;
TTextureCache& texture_cache;
TTBufferCache& buffer_cache;
TQueryCache& query_cache;
private:
void TryReleasePendingFences() {
- auto& gpu{system.GPU()};
- auto& memory_manager{gpu.MemoryManager()};
while (!fences.empty()) {
TFence& current_fence = fences.front();
if (ShouldWait() && !IsFenceSignaled(current_fence)) {
@@ -130,8 +127,8 @@ private:
}
PopAsyncFlushes();
if (current_fence->IsSemaphore()) {
- memory_manager.template Write<u32>(current_fence->GetAddress(),
- current_fence->GetPayload());
+ gpu_memory.template Write<u32>(current_fence->GetAddress(),
+ current_fence->GetPayload());
} else {
gpu.IncrementSyncPoint(current_fence->GetPayload());
}
diff --git a/src/video_core/gpu.cpp b/src/video_core/gpu.cpp
index acb6e6d46..ebd149c3a 100644
--- a/src/video_core/gpu.cpp
+++ b/src/video_core/gpu.cpp
@@ -27,9 +27,10 @@ namespace Tegra {
MICROPROFILE_DEFINE(GPU_wait, "GPU", "Wait for the GPU", MP_RGB(128, 128, 192));
-GPU::GPU(Core::System& system_, bool is_async_)
- : system{system_}, dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
- memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
+GPU::GPU(Core::System& system_, bool is_async_, bool use_nvdec_)
+ : system{system_}, memory_manager{std::make_unique<Tegra::MemoryManager>(system)},
+ dma_pusher{std::make_unique<Tegra::DmaPusher>(system, *this)},
+ cdma_pusher{std::make_unique<Tegra::CDmaPusher>(*this)}, use_nvdec{use_nvdec_},
maxwell_3d{std::make_unique<Engines::Maxwell3D>(system, *memory_manager)},
fermi_2d{std::make_unique<Engines::Fermi2D>()},
kepler_compute{std::make_unique<Engines::KeplerCompute>(system, *memory_manager)},
@@ -77,10 +78,18 @@ DmaPusher& GPU::DmaPusher() {
return *dma_pusher;
}
+Tegra::CDmaPusher& GPU::CDmaPusher() {
+ return *cdma_pusher;
+}
+
const DmaPusher& GPU::DmaPusher() const {
return *dma_pusher;
}
+const Tegra::CDmaPusher& GPU::CDmaPusher() const {
+ return *cdma_pusher;
+}
+
void GPU::WaitFence(u32 syncpoint_id, u32 value) {
// Synced GPU, is always in sync
if (!is_async) {
@@ -185,30 +194,6 @@ void GPU::SyncGuestHost() {
void GPU::OnCommandListEnd() {
renderer->Rasterizer().ReleaseFences();
}
-// Note that, traditionally, methods are treated as 4-byte addressable locations, and hence
-// their numbers are written down multiplied by 4 in Docs. Here we are not multiply by 4.
-// So the values you see in docs might be multiplied by 4.
-enum class BufferMethods {
- BindObject = 0x0,
- Nop = 0x2,
- SemaphoreAddressHigh = 0x4,
- SemaphoreAddressLow = 0x5,
- SemaphoreSequence = 0x6,
- SemaphoreTrigger = 0x7,
- NotifyIntr = 0x8,
- WrcacheFlush = 0x9,
- Unk28 = 0xA,
- UnkCacheFlush = 0xB,
- RefCnt = 0x14,
- SemaphoreAcquire = 0x1A,
- SemaphoreRelease = 0x1B,
- FenceValue = 0x1C,
- FenceAction = 0x1D,
- Unk78 = 0x1E,
- Unk7c = 0x1F,
- Yield = 0x20,
- NonPullerMethods = 0x40,
-};
enum class GpuSemaphoreOperation {
AcquireEqual = 0x1,
@@ -268,7 +253,12 @@ void GPU::CallPullerMethod(const MethodCall& method_call) {
case BufferMethods::UnkCacheFlush:
case BufferMethods::WrcacheFlush:
case BufferMethods::FenceValue:
+ break;
case BufferMethods::FenceAction:
+ ProcessFenceActionMethod();
+ break;
+ case BufferMethods::WaitForInterrupt:
+ ProcessWaitForInterruptMethod();
break;
case BufferMethods::SemaphoreTrigger: {
ProcessSemaphoreTriggerMethod();
@@ -382,6 +372,25 @@ void GPU::ProcessBindMethod(const MethodCall& method_call) {
}
}
+void GPU::ProcessFenceActionMethod() {
+ switch (regs.fence_action.op) {
+ case FenceOperation::Acquire:
+ WaitFence(regs.fence_action.syncpoint_id, regs.fence_value);
+ break;
+ case FenceOperation::Increment:
+ IncrementSyncPoint(regs.fence_action.syncpoint_id);
+ break;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented operation {}",
+ static_cast<u32>(regs.fence_action.op.Value()));
+ }
+}
+
+void GPU::ProcessWaitForInterruptMethod() {
+ // TODO(bunnei) ImplementMe
+ LOG_WARNING(HW_GPU, "(STUBBED) called");
+}
+
void GPU::ProcessSemaphoreTriggerMethod() {
const auto semaphoreOperationMask = 0xF;
const auto op =
diff --git a/src/video_core/gpu.h b/src/video_core/gpu.h
index c7d11deb2..21410e125 100644
--- a/src/video_core/gpu.h
+++ b/src/video_core/gpu.h
@@ -13,14 +13,15 @@
#include "common/common_types.h"
#include "core/hle/service/nvdrv/nvdata.h"
#include "core/hle/service/nvflinger/buffer_queue.h"
+#include "video_core/cdma_pusher.h"
#include "video_core/dma_pusher.h"
using CacheAddr = std::uintptr_t;
-inline CacheAddr ToCacheAddr(const void* host_ptr) {
+[[nodiscard]] inline CacheAddr ToCacheAddr(const void* host_ptr) {
return reinterpret_cast<CacheAddr>(host_ptr);
}
-inline u8* FromCacheAddr(CacheAddr cache_addr) {
+[[nodiscard]] inline u8* FromCacheAddr(CacheAddr cache_addr) {
return reinterpret_cast<u8*>(cache_addr);
}
@@ -148,16 +149,16 @@ public:
u32 subchannel{};
u32 method_count{};
- bool IsLastCall() const {
- return method_count <= 1;
- }
-
MethodCall(u32 method, u32 argument, u32 subchannel = 0, u32 method_count = 0)
: method(method), argument(argument), subchannel(subchannel),
method_count(method_count) {}
+
+ [[nodiscard]] bool IsLastCall() const {
+ return method_count <= 1;
+ }
};
- explicit GPU(Core::System& system, bool is_async);
+ explicit GPU(Core::System& system, bool is_async, bool use_nvdec);
virtual ~GPU();
/// Binds a renderer to the GPU.
@@ -178,10 +179,10 @@ public:
virtual void OnCommandListEnd();
/// Request a host GPU memory flush from the CPU.
- u64 RequestFlush(VAddr addr, std::size_t size);
+ [[nodiscard]] u64 RequestFlush(VAddr addr, std::size_t size);
/// Obtains current flush request fence id.
- u64 CurrentFlushRequestFence() const {
+ [[nodiscard]] u64 CurrentFlushRequestFence() const {
return current_flush_fence.load(std::memory_order_relaxed);
}
@@ -189,39 +190,52 @@ public:
void TickWork();
/// Returns a reference to the Maxwell3D GPU engine.
- Engines::Maxwell3D& Maxwell3D();
+ [[nodiscard]] Engines::Maxwell3D& Maxwell3D();
/// Returns a const reference to the Maxwell3D GPU engine.
- const Engines::Maxwell3D& Maxwell3D() const;
+ [[nodiscard]] const Engines::Maxwell3D& Maxwell3D() const;
/// Returns a reference to the KeplerCompute GPU engine.
- Engines::KeplerCompute& KeplerCompute();
+ [[nodiscard]] Engines::KeplerCompute& KeplerCompute();
/// Returns a reference to the KeplerCompute GPU engine.
- const Engines::KeplerCompute& KeplerCompute() const;
+ [[nodiscard]] const Engines::KeplerCompute& KeplerCompute() const;
/// Returns a reference to the GPU memory manager.
- Tegra::MemoryManager& MemoryManager();
+ [[nodiscard]] Tegra::MemoryManager& MemoryManager();
/// Returns a const reference to the GPU memory manager.
- const Tegra::MemoryManager& MemoryManager() const;
+ [[nodiscard]] const Tegra::MemoryManager& MemoryManager() const;
/// Returns a reference to the GPU DMA pusher.
- Tegra::DmaPusher& DmaPusher();
+ [[nodiscard]] Tegra::DmaPusher& DmaPusher();
- VideoCore::RendererBase& Renderer() {
+ /// Returns a const reference to the GPU DMA pusher.
+ [[nodiscard]] const Tegra::DmaPusher& DmaPusher() const;
+
+ /// Returns a reference to the GPU CDMA pusher.
+ [[nodiscard]] Tegra::CDmaPusher& CDmaPusher();
+
+ /// Returns a const reference to the GPU CDMA pusher.
+ [[nodiscard]] const Tegra::CDmaPusher& CDmaPusher() const;
+
+ /// Returns a reference to the underlying renderer.
+ [[nodiscard]] VideoCore::RendererBase& Renderer() {
return *renderer;
}
- const VideoCore::RendererBase& Renderer() const {
+ /// Returns a const reference to the underlying renderer.
+ [[nodiscard]] const VideoCore::RendererBase& Renderer() const {
return *renderer;
}
- VideoCore::ShaderNotify& ShaderNotify() {
+ /// Returns a reference to the shader notifier.
+ [[nodiscard]] VideoCore::ShaderNotify& ShaderNotify() {
return *shader_notify;
}
- const VideoCore::ShaderNotify& ShaderNotify() const {
+ /// Returns a const reference to the shader notifier.
+ [[nodiscard]] const VideoCore::ShaderNotify& ShaderNotify() const {
return *shader_notify;
}
@@ -233,24 +247,43 @@ public:
void IncrementSyncPoint(u32 syncpoint_id);
- u32 GetSyncpointValue(u32 syncpoint_id) const;
+ [[nodiscard]] u32 GetSyncpointValue(u32 syncpoint_id) const;
void RegisterSyncptInterrupt(u32 syncpoint_id, u32 value);
- bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
+ [[nodiscard]] bool CancelSyncptInterrupt(u32 syncpoint_id, u32 value);
- u64 GetTicks() const;
+ [[nodiscard]] u64 GetTicks() const;
- std::unique_lock<std::mutex> LockSync() {
+ [[nodiscard]] std::unique_lock<std::mutex> LockSync() {
return std::unique_lock{sync_mutex};
}
- bool IsAsync() const {
+ [[nodiscard]] bool IsAsync() const {
return is_async;
}
- /// Returns a const reference to the GPU DMA pusher.
- const Tegra::DmaPusher& DmaPusher() const;
+ [[nodiscard]] bool UseNvdec() const {
+ return use_nvdec;
+ }
+
+ enum class FenceOperation : u32 {
+ Acquire = 0,
+ Increment = 1,
+ };
+
+ union FenceAction {
+ u32 raw;
+ BitField<0, 1, FenceOperation> op;
+ BitField<8, 24, u32> syncpoint_id;
+
+ [[nodiscard]] static CommandHeader Build(FenceOperation op, u32 syncpoint_id) {
+ FenceAction result{};
+ result.op.Assign(op);
+ result.syncpoint_id.Assign(syncpoint_id);
+ return {result.raw};
+ }
+ };
struct Regs {
static constexpr size_t NUM_REGS = 0x40;
@@ -262,7 +295,7 @@ public:
u32 address_high;
u32 address_low;
- GPUVAddr SemaphoreAddress() const {
+ [[nodiscard]] GPUVAddr SemaphoreAddress() const {
return static_cast<GPUVAddr>((static_cast<GPUVAddr>(address_high) << 32) |
address_low);
}
@@ -280,10 +313,7 @@ public:
u32 semaphore_acquire;
u32 semaphore_release;
u32 fence_value;
- union {
- BitField<4, 4, u32> operation;
- BitField<8, 8, u32> id;
- } fence_action;
+ FenceAction fence_action;
INSERT_UNION_PADDING_WORDS(0xE2);
// Puller state
@@ -311,6 +341,9 @@ public:
/// Push GPU command entries to be processed
virtual void PushGPUEntries(Tegra::CommandList&& entries) = 0;
+ /// Push GPU command buffer entries to be processed
+ virtual void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) = 0;
+
/// Swap buffers (render frame)
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
@@ -328,6 +361,8 @@ protected:
private:
void ProcessBindMethod(const MethodCall& method_call);
+ void ProcessFenceActionMethod();
+ void ProcessWaitForInterruptMethod();
void ProcessSemaphoreTriggerMethod();
void ProcessSemaphoreRelease();
void ProcessSemaphoreAcquire();
@@ -343,16 +378,17 @@ private:
u32 methods_pending);
/// Determines where the method should be executed.
- bool ExecuteMethodOnEngine(u32 method);
+ [[nodiscard]] bool ExecuteMethodOnEngine(u32 method);
protected:
Core::System& system;
+ std::unique_ptr<Tegra::MemoryManager> memory_manager;
std::unique_ptr<Tegra::DmaPusher> dma_pusher;
+ std::unique_ptr<Tegra::CDmaPusher> cdma_pusher;
std::unique_ptr<VideoCore::RendererBase> renderer;
+ const bool use_nvdec;
private:
- std::unique_ptr<Tegra::MemoryManager> memory_manager;
-
/// Mapping of command subchannels to their bound engine ids
std::array<EngineID, 8> bound_engines = {};
/// 3D engine
@@ -373,6 +409,7 @@ private:
std::array<std::list<u32>, Service::Nvidia::MaxSyncPoints> syncpt_interrupts;
std::mutex sync_mutex;
+ std::mutex device_mutex;
std::condition_variable sync_cv;
diff --git a/src/video_core/gpu_asynch.cpp b/src/video_core/gpu_asynch.cpp
index 70a3d5738..a9baaf7ef 100644
--- a/src/video_core/gpu_asynch.cpp
+++ b/src/video_core/gpu_asynch.cpp
@@ -10,12 +10,13 @@
namespace VideoCommon {
-GPUAsynch::GPUAsynch(Core::System& system) : GPU{system, true}, gpu_thread{system} {}
+GPUAsynch::GPUAsynch(Core::System& system, bool use_nvdec)
+ : GPU{system, true, use_nvdec}, gpu_thread{system} {}
GPUAsynch::~GPUAsynch() = default;
void GPUAsynch::Start() {
- gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher);
+ gpu_thread.StartThread(*renderer, renderer->Context(), *dma_pusher, *cdma_pusher);
cpu_context = renderer->GetRenderWindow().CreateSharedContext();
cpu_context->MakeCurrent();
}
@@ -32,6 +33,27 @@ void GPUAsynch::PushGPUEntries(Tegra::CommandList&& entries) {
gpu_thread.SubmitList(std::move(entries));
}
+void GPUAsynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
+ if (!use_nvdec) {
+ return;
+ }
+ // This condition fires when a video stream ends, clear all intermediary data
+ if (entries[0].raw == 0xDEADB33F) {
+ cdma_pusher.reset();
+ return;
+ }
+ if (!cdma_pusher) {
+ cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
+ }
+
+ // SubmitCommandBuffer would make the nvdec operations async, this is not currently working
+ // TODO(ameerj): RE proper async nvdec operation
+ // gpu_thread.SubmitCommandBuffer(std::move(entries));
+
+ cdma_pusher->Push(std::move(entries));
+ cdma_pusher->DispatchCalls();
+}
+
void GPUAsynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
gpu_thread.SwapBuffers(framebuffer);
}
diff --git a/src/video_core/gpu_asynch.h b/src/video_core/gpu_asynch.h
index f89c855a5..0c0872e73 100644
--- a/src/video_core/gpu_asynch.h
+++ b/src/video_core/gpu_asynch.h
@@ -20,13 +20,14 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU asynchronously
class GPUAsynch final : public Tegra::GPU {
public:
- explicit GPUAsynch(Core::System& system);
+ explicit GPUAsynch(Core::System& system, bool use_nvdec);
~GPUAsynch() override;
void Start() override;
void ObtainContext() override;
void ReleaseContext() override;
void PushGPUEntries(Tegra::CommandList&& entries) override;
+ void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
void FlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override;
diff --git a/src/video_core/gpu_synch.cpp b/src/video_core/gpu_synch.cpp
index 1ca47ddef..ecf7bbdf3 100644
--- a/src/video_core/gpu_synch.cpp
+++ b/src/video_core/gpu_synch.cpp
@@ -7,7 +7,7 @@
namespace VideoCommon {
-GPUSynch::GPUSynch(Core::System& system) : GPU{system, false} {}
+GPUSynch::GPUSynch(Core::System& system, bool use_nvdec) : GPU{system, false, use_nvdec} {}
GPUSynch::~GPUSynch() = default;
@@ -26,6 +26,22 @@ void GPUSynch::PushGPUEntries(Tegra::CommandList&& entries) {
dma_pusher->DispatchCalls();
}
+void GPUSynch::PushCommandBuffer(Tegra::ChCommandHeaderList& entries) {
+ if (!use_nvdec) {
+ return;
+ }
+ // This condition fires when a video stream ends, clears all intermediary data
+ if (entries[0].raw == 0xDEADB33F) {
+ cdma_pusher.reset();
+ return;
+ }
+ if (!cdma_pusher) {
+ cdma_pusher = std::make_unique<Tegra::CDmaPusher>(*this);
+ }
+ cdma_pusher->Push(std::move(entries));
+ cdma_pusher->DispatchCalls();
+}
+
void GPUSynch::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
renderer->SwapBuffers(framebuffer);
}
diff --git a/src/video_core/gpu_synch.h b/src/video_core/gpu_synch.h
index 297258cb1..9d778c71a 100644
--- a/src/video_core/gpu_synch.h
+++ b/src/video_core/gpu_synch.h
@@ -19,13 +19,14 @@ namespace VideoCommon {
/// Implementation of GPU interface that runs the GPU synchronously
class GPUSynch final : public Tegra::GPU {
public:
- explicit GPUSynch(Core::System& system);
+ explicit GPUSynch(Core::System& system, bool use_nvdec);
~GPUSynch() override;
void Start() override;
void ObtainContext() override;
void ReleaseContext() override;
void PushGPUEntries(Tegra::CommandList&& entries) override;
+ void PushCommandBuffer(Tegra::ChCommandHeaderList& entries) override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
void FlushRegion(VAddr addr, u64 size) override;
void InvalidateRegion(VAddr addr, u64 size) override;
diff --git a/src/video_core/gpu_thread.cpp b/src/video_core/gpu_thread.cpp
index bf761abf2..4b8f58283 100644
--- a/src/video_core/gpu_thread.cpp
+++ b/src/video_core/gpu_thread.cpp
@@ -18,7 +18,7 @@ namespace VideoCommon::GPUThread {
/// Runs the GPU thread
static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context, Tegra::DmaPusher& dma_pusher,
- SynchState& state) {
+ SynchState& state, Tegra::CDmaPusher& cdma_pusher) {
std::string name = "yuzu:GPU";
MicroProfileOnThreadCreate(name.c_str());
Common::SetCurrentThreadName(name.c_str());
@@ -42,6 +42,10 @@ static void RunThread(Core::System& system, VideoCore::RendererBase& renderer,
if (const auto submit_list = std::get_if<SubmitListCommand>(&next.data)) {
dma_pusher.Push(std::move(submit_list->entries));
dma_pusher.DispatchCalls();
+ } else if (const auto command_list = std::get_if<SubmitChCommandEntries>(&next.data)) {
+ // NVDEC
+ cdma_pusher.Push(std::move(command_list->entries));
+ cdma_pusher.DispatchCalls();
} else if (const auto data = std::get_if<SwapBuffersCommand>(&next.data)) {
renderer.SwapBuffers(data->framebuffer ? &*data->framebuffer : nullptr);
} else if (std::holds_alternative<OnCommandListEndCommand>(next.data)) {
@@ -75,15 +79,19 @@ ThreadManager::~ThreadManager() {
void ThreadManager::StartThread(VideoCore::RendererBase& renderer,
Core::Frontend::GraphicsContext& context,
- Tegra::DmaPusher& dma_pusher) {
- thread = std::thread{RunThread, std::ref(system), std::ref(renderer),
- std::ref(context), std::ref(dma_pusher), std::ref(state)};
+ Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher) {
+ thread = std::thread(RunThread, std::ref(system), std::ref(renderer), std::ref(context),
+ std::ref(dma_pusher), std::ref(state), std::ref(cdma_pusher));
}
void ThreadManager::SubmitList(Tegra::CommandList&& entries) {
PushCommand(SubmitListCommand(std::move(entries)));
}
+void ThreadManager::SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries) {
+ PushCommand(SubmitChCommandEntries(std::move(entries)));
+}
+
void ThreadManager::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
PushCommand(SwapBuffersCommand(framebuffer ? std::make_optional(*framebuffer) : std::nullopt));
}
diff --git a/src/video_core/gpu_thread.h b/src/video_core/gpu_thread.h
index 5a28335d6..32a34e3a7 100644
--- a/src/video_core/gpu_thread.h
+++ b/src/video_core/gpu_thread.h
@@ -37,6 +37,14 @@ struct SubmitListCommand final {
Tegra::CommandList entries;
};
+/// Command to signal to the GPU thread that a cdma command list is ready for processing
+struct SubmitChCommandEntries final {
+ explicit SubmitChCommandEntries(Tegra::ChCommandHeaderList&& entries)
+ : entries{std::move(entries)} {}
+
+ Tegra::ChCommandHeaderList entries;
+};
+
/// Command to signal to the GPU thread that a swap buffers is pending
struct SwapBuffersCommand final {
explicit SwapBuffersCommand(std::optional<const Tegra::FramebufferConfig> framebuffer)
@@ -77,9 +85,9 @@ struct OnCommandListEndCommand final {};
struct GPUTickCommand final {};
using CommandData =
- std::variant<EndProcessingCommand, SubmitListCommand, SwapBuffersCommand, FlushRegionCommand,
- InvalidateRegionCommand, FlushAndInvalidateRegionCommand, OnCommandListEndCommand,
- GPUTickCommand>;
+ std::variant<EndProcessingCommand, SubmitListCommand, SubmitChCommandEntries,
+ SwapBuffersCommand, FlushRegionCommand, InvalidateRegionCommand,
+ FlushAndInvalidateRegionCommand, OnCommandListEndCommand, GPUTickCommand>;
struct CommandDataContainer {
CommandDataContainer() = default;
@@ -109,11 +117,14 @@ public:
/// Creates and starts the GPU thread.
void StartThread(VideoCore::RendererBase& renderer, Core::Frontend::GraphicsContext& context,
- Tegra::DmaPusher& dma_pusher);
+ Tegra::DmaPusher& dma_pusher, Tegra::CDmaPusher& cdma_pusher);
/// Push GPU command entries to be processed
void SubmitList(Tegra::CommandList&& entries);
+ /// Push GPU CDMA command buffer entries to be processed
+ void SubmitCommandBuffer(Tegra::ChCommandHeaderList&& entries);
+
/// Swap buffers (render frame)
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer);
diff --git a/src/video_core/host_shaders/CMakeLists.txt b/src/video_core/host_shaders/CMakeLists.txt
new file mode 100644
index 000000000..c157724a9
--- /dev/null
+++ b/src/video_core/host_shaders/CMakeLists.txt
@@ -0,0 +1,36 @@
+set(SHADER_SOURCES
+ opengl_present.frag
+ opengl_present.vert
+)
+
+set(SHADER_INCLUDE ${CMAKE_CURRENT_BINARY_DIR}/include)
+set(SHADER_DIR ${SHADER_INCLUDE}/video_core/host_shaders)
+set(HOST_SHADERS_INCLUDE ${SHADER_INCLUDE} PARENT_SCOPE)
+
+set(INPUT_FILE ${CMAKE_CURRENT_SOURCE_DIR}/source_shader.h.in)
+set(HEADER_GENERATOR ${CMAKE_CURRENT_SOURCE_DIR}/StringShaderHeader.cmake)
+
+foreach(FILENAME IN ITEMS ${SHADER_SOURCES})
+ string(REPLACE "." "_" SHADER_NAME ${FILENAME})
+ set(SOURCE_FILE ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME})
+ set(HEADER_FILE ${SHADER_DIR}/${SHADER_NAME}.h)
+ add_custom_command(
+ OUTPUT
+ ${HEADER_FILE}
+ COMMAND
+ ${CMAKE_COMMAND} -P ${HEADER_GENERATOR} ${SOURCE_FILE} ${HEADER_FILE} ${INPUT_FILE}
+ MAIN_DEPENDENCY
+ ${SOURCE_FILE}
+ DEPENDS
+ ${INPUT_FILE}
+ # HEADER_GENERATOR should be included here but msbuild seems to assume it's always modified
+ )
+ set(SHADER_HEADERS ${SHADER_HEADERS} ${HEADER_FILE})
+endforeach()
+
+add_custom_target(host_shaders
+ DEPENDS
+ ${SHADER_HEADERS}
+ SOURCES
+ ${SHADER_SOURCES}
+)
diff --git a/src/video_core/host_shaders/StringShaderHeader.cmake b/src/video_core/host_shaders/StringShaderHeader.cmake
new file mode 100644
index 000000000..c0fc49768
--- /dev/null
+++ b/src/video_core/host_shaders/StringShaderHeader.cmake
@@ -0,0 +1,13 @@
+set(SOURCE_FILE ${CMAKE_ARGV3})
+set(HEADER_FILE ${CMAKE_ARGV4})
+set(INPUT_FILE ${CMAKE_ARGV5})
+
+get_filename_component(CONTENTS_NAME ${SOURCE_FILE} NAME)
+string(REPLACE "." "_" CONTENTS_NAME ${CONTENTS_NAME})
+string(TOUPPER ${CONTENTS_NAME} CONTENTS_NAME)
+
+file(READ ${SOURCE_FILE} CONTENTS)
+
+get_filename_component(OUTPUT_DIR ${HEADER_FILE} DIRECTORY)
+make_directory(${OUTPUT_DIR})
+configure_file(${INPUT_FILE} ${HEADER_FILE} @ONLY)
diff --git a/src/video_core/host_shaders/opengl_present.frag b/src/video_core/host_shaders/opengl_present.frag
new file mode 100644
index 000000000..8a4cb024b
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.frag
@@ -0,0 +1,10 @@
+#version 430 core
+
+layout (location = 0) in vec2 frag_tex_coord;
+layout (location = 0) out vec4 color;
+
+layout (binding = 0) uniform sampler2D color_texture;
+
+void main() {
+ color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
+}
diff --git a/src/video_core/host_shaders/opengl_present.vert b/src/video_core/host_shaders/opengl_present.vert
new file mode 100644
index 000000000..2235d31a4
--- /dev/null
+++ b/src/video_core/host_shaders/opengl_present.vert
@@ -0,0 +1,24 @@
+#version 430 core
+
+out gl_PerVertex {
+ vec4 gl_Position;
+};
+
+layout (location = 0) in vec2 vert_position;
+layout (location = 1) in vec2 vert_tex_coord;
+layout (location = 0) out vec2 frag_tex_coord;
+
+// This is a truncated 3x3 matrix for 2D transformations:
+// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
+// The third column performs translation.
+// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
+// implicitly be [0, 0, 1]
+layout (location = 0) uniform mat3x2 modelview_matrix;
+
+void main() {
+ // Multiply input position by the rotscale part of the matrix and then manually translate by
+ // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
+ // to `vec3(vert_position.xy, 1.0)`
+ gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
+ frag_tex_coord = vert_tex_coord;
+}
diff --git a/src/video_core/host_shaders/source_shader.h.in b/src/video_core/host_shaders/source_shader.h.in
new file mode 100644
index 000000000..ccdb0d2a9
--- /dev/null
+++ b/src/video_core/host_shaders/source_shader.h.in
@@ -0,0 +1,9 @@
+#pragma once
+
+#include <string_view>
+
+namespace HostShaders {
+
+constexpr std::string_view @CONTENTS_NAME@ = R"(@CONTENTS@)";
+
+} // namespace HostShaders
diff --git a/src/video_core/macro/macro.cpp b/src/video_core/macro/macro.cpp
index a50e7b4e0..cd21a2112 100644
--- a/src/video_core/macro/macro.cpp
+++ b/src/video_core/macro/macro.cpp
@@ -36,7 +36,7 @@ void MacroEngine::Execute(Engines::Maxwell3D& maxwell3d, u32 method,
}
} else {
// Macro not compiled, check if it's uploaded and if so, compile it
- std::optional<u32> mid_method = std::nullopt;
+ std::optional<u32> mid_method;
const auto macro_code = uploaded_macro_code.find(method);
if (macro_code == uploaded_macro_code.end()) {
for (const auto& [method_base, code] : uploaded_macro_code) {
diff --git a/src/video_core/macro/macro_jit_x64.cpp b/src/video_core/macro/macro_jit_x64.cpp
index c1b9e4ad9..954b87515 100644
--- a/src/video_core/macro/macro_jit_x64.cpp
+++ b/src/video_core/macro/macro_jit_x64.cpp
@@ -14,11 +14,11 @@ MICROPROFILE_DEFINE(MacroJitCompile, "GPU", "Compile macro JIT", MP_RGB(173, 255
MICROPROFILE_DEFINE(MacroJitExecute, "GPU", "Execute macro JIT", MP_RGB(255, 255, 0));
namespace Tegra {
-static const Xbyak::Reg64 STATE = Xbyak::util::rbx;
-static const Xbyak::Reg32 RESULT = Xbyak::util::ebp;
-static const Xbyak::Reg64 PARAMETERS = Xbyak::util::r12;
-static const Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d;
-static const Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
+constexpr Xbyak::Reg64 STATE = Xbyak::util::rbx;
+constexpr Xbyak::Reg32 RESULT = Xbyak::util::ebp;
+constexpr Xbyak::Reg64 PARAMETERS = Xbyak::util::r12;
+constexpr Xbyak::Reg32 METHOD_ADDRESS = Xbyak::util::r14d;
+constexpr Xbyak::Reg64 BRANCH_HOLDER = Xbyak::util::r15;
static const std::bitset<32> PERSISTENT_REGISTERS = Common::X64::BuildRegSet({
STATE,
diff --git a/src/video_core/memory_manager.cpp b/src/video_core/memory_manager.cpp
index c217f5bb2..6e70bd362 100644
--- a/src/video_core/memory_manager.cpp
+++ b/src/video_core/memory_manager.cpp
@@ -11,6 +11,7 @@
#include "video_core/gpu.h"
#include "video_core/memory_manager.h"
#include "video_core/rasterizer_interface.h"
+#include "video_core/renderer_base.h"
namespace Tegra {
@@ -44,6 +45,12 @@ GPUVAddr MemoryManager::MapAllocate(VAddr cpu_addr, std::size_t size, std::size_
return Map(cpu_addr, *FindFreeRange(size, align), size);
}
+GPUVAddr MemoryManager::MapAllocate32(VAddr cpu_addr, std::size_t size) {
+ const std::optional<GPUVAddr> gpu_addr = FindFreeRange(size, 1, true);
+ ASSERT(gpu_addr);
+ return Map(cpu_addr, *gpu_addr, size);
+}
+
void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
if (!size) {
return;
@@ -58,7 +65,7 @@ void MemoryManager::Unmap(GPUVAddr gpu_addr, std::size_t size) {
std::optional<GPUVAddr> MemoryManager::AllocateFixed(GPUVAddr gpu_addr, std::size_t size) {
for (u64 offset{}; offset < size; offset += page_size) {
if (!GetPageEntry(gpu_addr + offset).IsUnmapped()) {
- return {};
+ return std::nullopt;
}
}
@@ -108,7 +115,8 @@ void MemoryManager::SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::s
page_table[PageEntryIndex(gpu_addr)] = page_entry;
}
-std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align) const {
+std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size_t align,
+ bool start_32bit_address) const {
if (!align) {
align = page_size;
} else {
@@ -116,7 +124,7 @@ std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size
}
u64 available_size{};
- GPUVAddr gpu_addr{address_space_start};
+ GPUVAddr gpu_addr{start_32bit_address ? address_space_start_low : address_space_start};
while (gpu_addr + available_size < address_space_size) {
if (GetPageEntry(gpu_addr + available_size).IsUnmapped()) {
available_size += page_size;
@@ -135,13 +143,13 @@ std::optional<GPUVAddr> MemoryManager::FindFreeRange(std::size_t size, std::size
}
}
- return {};
+ return std::nullopt;
}
std::optional<VAddr> MemoryManager::GpuToCpuAddress(GPUVAddr gpu_addr) const {
const auto page_entry{GetPageEntry(gpu_addr)};
if (!page_entry.IsValid()) {
- return {};
+ return std::nullopt;
}
return page_entry.ToAddress() + (gpu_addr & page_mask);
@@ -316,10 +324,10 @@ void MemoryManager::CopyBlockUnsafe(GPUVAddr gpu_dest_addr, GPUVAddr gpu_src_add
WriteBlockUnsafe(gpu_dest_addr, tmp_buffer.data(), size);
}
-bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) {
+bool MemoryManager::IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const {
const auto cpu_addr{GpuToCpuAddress(gpu_addr)};
if (!cpu_addr) {
- return {};
+ return false;
}
const std::size_t page{(*cpu_addr & Core::Memory::PAGE_MASK) + size};
return page <= Core::Memory::PAGE_SIZE;
diff --git a/src/video_core/memory_manager.h b/src/video_core/memory_manager.h
index 8953fcb53..c078193d9 100644
--- a/src/video_core/memory_manager.h
+++ b/src/video_core/memory_manager.h
@@ -31,19 +31,19 @@ public:
constexpr PageEntry(State state) : state{state} {}
constexpr PageEntry(VAddr addr) : state{static_cast<State>(addr >> ShiftBits)} {}
- constexpr bool IsUnmapped() const {
+ [[nodiscard]] constexpr bool IsUnmapped() const {
return state == State::Unmapped;
}
- constexpr bool IsAllocated() const {
+ [[nodiscard]] constexpr bool IsAllocated() const {
return state == State::Allocated;
}
- constexpr bool IsValid() const {
+ [[nodiscard]] constexpr bool IsValid() const {
return !IsUnmapped() && !IsAllocated();
}
- constexpr VAddr ToAddress() const {
+ [[nodiscard]] constexpr VAddr ToAddress() const {
if (!IsValid()) {
return {};
}
@@ -51,7 +51,7 @@ public:
return static_cast<VAddr>(state) << ShiftBits;
}
- constexpr PageEntry operator+(u64 offset) {
+ [[nodiscard]] constexpr PageEntry operator+(u64 offset) const {
// If this is a reserved value, offsets do not apply
if (!IsValid()) {
return *this;
@@ -74,16 +74,16 @@ public:
/// Binds a renderer to the memory manager.
void BindRasterizer(VideoCore::RasterizerInterface& rasterizer);
- std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
+ [[nodiscard]] std::optional<VAddr> GpuToCpuAddress(GPUVAddr addr) const;
template <typename T>
- T Read(GPUVAddr addr) const;
+ [[nodiscard]] T Read(GPUVAddr addr) const;
template <typename T>
void Write(GPUVAddr addr, T data);
- u8* GetPointer(GPUVAddr addr);
- const u8* GetPointer(GPUVAddr addr) const;
+ [[nodiscard]] u8* GetPointer(GPUVAddr addr);
+ [[nodiscard]] const u8* GetPointer(GPUVAddr addr) const;
/**
* ReadBlock and WriteBlock are full read and write operations over virtual
@@ -112,29 +112,32 @@ public:
/**
* IsGranularRange checks if a gpu region can be simply read with a pointer.
*/
- bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size);
+ [[nodiscard]] bool IsGranularRange(GPUVAddr gpu_addr, std::size_t size) const;
- GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
- GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
- std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
- GPUVAddr Allocate(std::size_t size, std::size_t align);
+ [[nodiscard]] GPUVAddr Map(VAddr cpu_addr, GPUVAddr gpu_addr, std::size_t size);
+ [[nodiscard]] GPUVAddr MapAllocate(VAddr cpu_addr, std::size_t size, std::size_t align);
+ [[nodiscard]] GPUVAddr MapAllocate32(VAddr cpu_addr, std::size_t size);
+ [[nodiscard]] std::optional<GPUVAddr> AllocateFixed(GPUVAddr gpu_addr, std::size_t size);
+ [[nodiscard]] GPUVAddr Allocate(std::size_t size, std::size_t align);
void Unmap(GPUVAddr gpu_addr, std::size_t size);
private:
- PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
+ [[nodiscard]] PageEntry GetPageEntry(GPUVAddr gpu_addr) const;
void SetPageEntry(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size = page_size);
GPUVAddr UpdateRange(GPUVAddr gpu_addr, PageEntry page_entry, std::size_t size);
- std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align) const;
+ [[nodiscard]] std::optional<GPUVAddr> FindFreeRange(std::size_t size, std::size_t align,
+ bool start_32bit_address = false) const;
void TryLockPage(PageEntry page_entry, std::size_t size);
void TryUnlockPage(PageEntry page_entry, std::size_t size);
- static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
+ [[nodiscard]] static constexpr std::size_t PageEntryIndex(GPUVAddr gpu_addr) {
return (gpu_addr >> page_bits) & page_table_mask;
}
static constexpr u64 address_space_size = 1ULL << 40;
static constexpr u64 address_space_start = 1ULL << 32;
+ static constexpr u64 address_space_start_low = 1ULL << 16;
static constexpr u64 page_bits{16};
static constexpr u64 page_size{1 << page_bits};
static constexpr u64 page_mask{page_size - 1};
diff --git a/src/video_core/query_cache.h b/src/video_core/query_cache.h
index 0d3a88765..fc54ca0ef 100644
--- a/src/video_core/query_cache.h
+++ b/src/video_core/query_cache.h
@@ -91,14 +91,15 @@ private:
std::shared_ptr<HostCounter> last;
};
-template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter,
- class QueryPool>
+template <class QueryCache, class CachedQuery, class CounterStream, class HostCounter>
class QueryCacheBase {
public:
- explicit QueryCacheBase(Core::System& system, VideoCore::RasterizerInterface& rasterizer)
- : system{system}, rasterizer{rasterizer}, streams{{CounterStream{
- static_cast<QueryCache&>(*this),
- VideoCore::QueryType::SamplesPassed}}} {}
+ explicit QueryCacheBase(VideoCore::RasterizerInterface& rasterizer_,
+ Tegra::Engines::Maxwell3D& maxwell3d_,
+ Tegra::MemoryManager& gpu_memory_)
+ : rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
+ gpu_memory{gpu_memory_}, streams{{CounterStream{static_cast<QueryCache&>(*this),
+ VideoCore::QueryType::SamplesPassed}}} {}
void InvalidateRegion(VAddr addr, std::size_t size) {
std::unique_lock lock{mutex};
@@ -118,29 +119,27 @@ public:
*/
void Query(GPUVAddr gpu_addr, VideoCore::QueryType type, std::optional<u64> timestamp) {
std::unique_lock lock{mutex};
- auto& memory_manager = system.GPU().MemoryManager();
- const std::optional<VAddr> cpu_addr_opt = memory_manager.GpuToCpuAddress(gpu_addr);
- ASSERT(cpu_addr_opt);
- VAddr cpu_addr = *cpu_addr_opt;
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
+ ASSERT(cpu_addr);
- CachedQuery* query = TryGet(cpu_addr);
+ CachedQuery* query = TryGet(*cpu_addr);
if (!query) {
- ASSERT_OR_EXECUTE(cpu_addr_opt, return;);
- const auto host_ptr = memory_manager.GetPointer(gpu_addr);
+ ASSERT_OR_EXECUTE(cpu_addr, return;);
+ u8* const host_ptr = gpu_memory.GetPointer(gpu_addr);
- query = Register(type, cpu_addr, host_ptr, timestamp.has_value());
+ query = Register(type, *cpu_addr, host_ptr, timestamp.has_value());
}
query->BindCounter(Stream(type).Current(), timestamp);
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
- AsyncFlushQuery(cpu_addr);
+ AsyncFlushQuery(*cpu_addr);
}
}
/// Updates counters from GPU state. Expected to be called once per draw, clear or dispatch.
void UpdateCounters() {
std::unique_lock lock{mutex};
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
Stream(VideoCore::QueryType::SamplesPassed).Update(regs.samplecnt_enable);
}
@@ -206,9 +205,6 @@ public:
committed_flushes.pop_front();
}
-protected:
- std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
-
private:
/// Flushes a memory range to guest memory and removes it from the cache.
void FlushAndRemoveRegion(VAddr addr, std::size_t size) {
@@ -270,8 +266,9 @@ private:
static constexpr std::uintptr_t PAGE_SIZE = 4096;
static constexpr unsigned PAGE_BITS = 12;
- Core::System& system;
VideoCore::RasterizerInterface& rasterizer;
+ Tegra::Engines::Maxwell3D& maxwell3d;
+ Tegra::MemoryManager& gpu_memory;
std::recursive_mutex mutex;
diff --git a/src/video_core/rasterizer_interface.h b/src/video_core/rasterizer_interface.h
index 3cbdac8e7..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
class RasterizerInterface {
public:
- virtual ~RasterizerInterface() {}
+ virtual ~RasterizerInterface() = default;
/// Dispatches a draw invocation
virtual void Draw(bool is_indexed, bool is_instanced) = 0;
@@ -90,15 +90,16 @@ public:
virtual void TickFrame() = 0;
/// Attempt to use a faster method to perform a surface copy
- virtual bool AccelerateSurfaceCopy(const Tegra::Engines::Fermi2D::Regs::Surface& src,
- const Tegra::Engines::Fermi2D::Regs::Surface& dst,
- const Tegra::Engines::Fermi2D::Config& copy_config) {
+ [[nodiscard]] virtual bool AccelerateSurfaceCopy(
+ const Tegra::Engines::Fermi2D::Regs::Surface& src,
+ const Tegra::Engines::Fermi2D::Regs::Surface& dst,
+ const Tegra::Engines::Fermi2D::Config& copy_config) {
return false;
}
/// Attempt to use a faster method to display the framebuffer to screen
- virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
- u32 pixel_stride) {
+ [[nodiscard]] virtual bool AccelerateDisplay(const Tegra::FramebufferConfig& config,
+ VAddr framebuffer_addr, u32 pixel_stride) {
return false;
}
@@ -106,19 +107,16 @@ public:
virtual void UpdatePagesCachedCount(VAddr addr, u64 size, int delta) {}
/// Initialize disk cached resources for the game being emulated
- virtual void LoadDiskResources(const std::atomic_bool& stop_loading = false,
- const DiskResourceLoadCallback& callback = {}) {}
-
- /// Initializes renderer dirty flags
- virtual void SetupDirtyFlags() {}
+ virtual void LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading,
+ const DiskResourceLoadCallback& callback) {}
/// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
- GuestDriverProfile& AccessGuestDriverProfile() {
+ [[nodiscard]] GuestDriverProfile& AccessGuestDriverProfile() {
return guest_driver_profile;
}
/// Grant access to the Guest Driver Profile for recording/obtaining info on the guest driver.
- const GuestDriverProfile& AccessGuestDriverProfile() const {
+ [[nodiscard]] const GuestDriverProfile& AccessGuestDriverProfile() const {
return guest_driver_profile;
}
diff --git a/src/video_core/renderer_base.h b/src/video_core/renderer_base.h
index 649074acd..51dde8eb5 100644
--- a/src/video_core/renderer_base.h
+++ b/src/video_core/renderer_base.h
@@ -38,7 +38,7 @@ public:
virtual ~RendererBase();
/// Initialize the renderer
- virtual bool Init() = 0;
+ [[nodiscard]] virtual bool Init() = 0;
/// Shutdown the renderer
virtual void ShutDown() = 0;
@@ -46,51 +46,46 @@ public:
/// Finalize rendering the guest frame and draw into the presentation texture
virtual void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) = 0;
- /// Draws the latest frame to the window waiting timeout_ms for a frame to arrive (Renderer
- /// specific implementation)
- /// Returns true if a frame was drawn
- virtual bool TryPresent(int timeout_ms) = 0;
-
// Getter/setter functions:
// ------------------------
- f32 GetCurrentFPS() const {
+ [[nodiscard]] f32 GetCurrentFPS() const {
return m_current_fps;
}
- int GetCurrentFrame() const {
+ [[nodiscard]] int GetCurrentFrame() const {
return m_current_frame;
}
- RasterizerInterface& Rasterizer() {
+ [[nodiscard]] RasterizerInterface& Rasterizer() {
return *rasterizer;
}
- const RasterizerInterface& Rasterizer() const {
+ [[nodiscard]] const RasterizerInterface& Rasterizer() const {
return *rasterizer;
}
- Core::Frontend::GraphicsContext& Context() {
+ [[nodiscard]] Core::Frontend::GraphicsContext& Context() {
return *context;
}
- const Core::Frontend::GraphicsContext& Context() const {
+ [[nodiscard]] const Core::Frontend::GraphicsContext& Context() const {
return *context;
}
- Core::Frontend::EmuWindow& GetRenderWindow() {
+ [[nodiscard]] Core::Frontend::EmuWindow& GetRenderWindow() {
return render_window;
}
- const Core::Frontend::EmuWindow& GetRenderWindow() const {
+ [[nodiscard]] const Core::Frontend::EmuWindow& GetRenderWindow() const {
return render_window;
}
- RendererSettings& Settings() {
+ [[nodiscard]] RendererSettings& Settings() {
return renderer_settings;
}
- const RendererSettings& Settings() const {
+ [[nodiscard]] const RendererSettings& Settings() const {
return renderer_settings;
}
diff --git a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
index b7e9ed2e9..d6120c23e 100644
--- a/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_arb_decompiler.cpp
@@ -39,8 +39,8 @@ using Operation = const OperationNode&;
constexpr std::array INTERNAL_FLAG_NAMES = {"ZERO", "SIGN", "CARRY", "OVERFLOW"};
char Swizzle(std::size_t component) {
- ASSERT(component < 4);
- return component["xyzw"];
+ static constexpr std::string_view SWIZZLE{"xyzw"};
+ return SWIZZLE.at(component);
}
constexpr bool IsGenericAttribute(Attribute::Index index) {
@@ -224,7 +224,7 @@ private:
std::string Visit(const Node& node);
- std::pair<std::string, std::size_t> BuildCoords(Operation);
+ std::tuple<std::string, std::string, std::size_t> BuildCoords(Operation);
std::string BuildAoffi(Operation);
std::string GlobalMemoryPointer(const GmemNode& gmem);
void Exit();
@@ -376,9 +376,11 @@ private:
std::string temporary = AllocTemporary();
std::string address;
std::string_view opname;
+ bool robust = false;
if (const auto gmem = std::get_if<GmemNode>(&*operation[0])) {
address = GlobalMemoryPointer(*gmem);
opname = "ATOM";
+ robust = true;
} else if (const auto smem = std::get_if<SmemNode>(&*operation[0])) {
address = fmt::format("shared_mem[{}]", Visit(smem->GetAddress()));
opname = "ATOMS";
@@ -386,7 +388,15 @@ private:
UNREACHABLE();
return "{0, 0, 0, 0}";
}
+ if (robust) {
+ AddLine("IF NE.x;");
+ }
AddLine("{}.{}.{} {}, {}, {};", opname, op, type, temporary, Visit(operation[1]), address);
+ if (robust) {
+ AddLine("ELSE;");
+ AddLine("MOV.S {}, 0;", temporary);
+ AddLine("ENDIF;");
+ }
return temporary;
}
@@ -980,10 +990,9 @@ void ARBDecompiler::DeclareLocalMemory() {
}
void ARBDecompiler::DeclareGlobalMemory() {
- const std::size_t num_entries = ir.GetGlobalMemory().size();
+ const size_t num_entries = ir.GetGlobalMemory().size();
if (num_entries > 0) {
- const std::size_t num_vectors = Common::AlignUp(num_entries, 2) / 2;
- AddLine("PARAM c[{}] = {{ program.local[0..{}] }};", num_vectors, num_vectors - 1);
+ AddLine("PARAM c[{}] = {{ program.local[0..{}] }};", num_entries, num_entries - 1);
}
}
@@ -1363,7 +1372,8 @@ std::string ARBDecompiler::Visit(const Node& node) {
if (const auto gmem = std::get_if<GmemNode>(&*node)) {
std::string temporary = AllocTemporary();
- AddLine("LOAD.U32 {}, {};", temporary, GlobalMemoryPointer(*gmem));
+ AddLine("MOV {}, 0;", temporary);
+ AddLine("LOAD.U32 {} (NE.x), {};", temporary, GlobalMemoryPointer(*gmem));
return temporary;
}
@@ -1406,12 +1416,12 @@ std::string ARBDecompiler::Visit(const Node& node) {
return {};
}
-std::pair<std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operation) {
+std::tuple<std::string, std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operation) {
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
UNIMPLEMENTED_IF(meta.sampler.is_indexed);
- UNIMPLEMENTED_IF(meta.sampler.is_shadow && meta.sampler.is_array &&
- meta.sampler.type == Tegra::Shader::TextureType::TextureCube);
+ const bool is_extended = meta.sampler.is_shadow && meta.sampler.is_array &&
+ meta.sampler.type == Tegra::Shader::TextureType::TextureCube;
const std::size_t count = operation.GetOperandsCount();
std::string temporary = AllocVectorTemporary();
std::size_t i = 0;
@@ -1419,12 +1429,21 @@ std::pair<std::string, std::size_t> ARBDecompiler::BuildCoords(Operation operati
AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), Visit(operation[i]));
}
if (meta.sampler.is_array) {
- AddLine("I2F.S {}.{}, {};", temporary, Swizzle(i++), Visit(meta.array));
+ AddLine("I2F.S {}.{}, {};", temporary, Swizzle(i), Visit(meta.array));
+ ++i;
}
if (meta.sampler.is_shadow) {
- AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i++), Visit(meta.depth_compare));
+ std::string compare = Visit(meta.depth_compare);
+ if (is_extended) {
+ ASSERT(i == 4);
+ std::string extra_coord = AllocVectorTemporary();
+ AddLine("MOV.F {}.x, {};", extra_coord, compare);
+ return {fmt::format("{}, {}", temporary, extra_coord), extra_coord, 0};
+ }
+ AddLine("MOV.F {}.{}, {};", temporary, Swizzle(i), compare);
+ ++i;
}
- return {std::move(temporary), i};
+ return {temporary, temporary, i};
}
std::string ARBDecompiler::BuildAoffi(Operation operation) {
@@ -1441,18 +1460,21 @@ std::string ARBDecompiler::BuildAoffi(Operation operation) {
}
std::string ARBDecompiler::GlobalMemoryPointer(const GmemNode& gmem) {
+ // Read a bindless SSBO, return its address and set CC accordingly
+ // address = c[binding].xy
+ // length = c[binding].z
const u32 binding = global_memory_names.at(gmem.GetDescriptor());
- const char result_swizzle = binding % 2 == 0 ? 'x' : 'y';
const std::string pointer = AllocLongVectorTemporary();
std::string temporary = AllocTemporary();
- const u32 local_index = binding / 2;
- AddLine("PK64.U {}, c[{}];", pointer, local_index);
+ AddLine("PK64.U {}, c[{}];", pointer, binding);
AddLine("SUB.U {}, {}, {};", temporary, Visit(gmem.GetRealAddress()),
Visit(gmem.GetBaseAddress()));
AddLine("CVT.U64.U32 {}.z, {};", pointer, temporary);
- AddLine("ADD.U64 {}.x, {}.{}, {}.z;", pointer, pointer, result_swizzle, pointer);
+ AddLine("ADD.U64 {}.x, {}.x, {}.z;", pointer, pointer, pointer);
+ // Compare offset to length and set CC
+ AddLine("SLT.U.CC RC.x, {}, c[{}].z;", temporary, binding);
return fmt::format("{}.x", pointer);
}
@@ -1552,7 +1574,9 @@ std::string ARBDecompiler::Assign(Operation operation) {
ResetTemporaries();
return {};
} else if (const auto gmem = std::get_if<GmemNode>(&*dest)) {
+ AddLine("IF NE.x;");
AddLine("STORE.U32 {}, {};", Visit(src), GlobalMemoryPointer(*gmem));
+ AddLine("ENDIF;");
ResetTemporaries();
return {};
} else {
@@ -1844,7 +1868,7 @@ std::string ARBDecompiler::LogicalAddCarry(Operation operation) {
std::string ARBDecompiler::Texture(Operation operation) {
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
- const auto [temporary, swizzle] = BuildCoords(operation);
+ const auto [coords, temporary, swizzle] = BuildCoords(operation);
std::string_view opcode = "TEX";
std::string extra;
@@ -1873,7 +1897,7 @@ std::string ARBDecompiler::Texture(Operation operation) {
}
}
- AddLine("{}.F {}, {},{} texture[{}], {}{};", opcode, temporary, temporary, extra, sampler_id,
+ AddLine("{}.F {}, {},{} texture[{}], {}{};", opcode, temporary, coords, extra, sampler_id,
TextureType(meta), BuildAoffi(operation));
AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
return fmt::format("{}.x", temporary);
@@ -1882,7 +1906,7 @@ std::string ARBDecompiler::Texture(Operation operation) {
std::string ARBDecompiler::TextureGather(Operation operation) {
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
- const auto [temporary, swizzle] = BuildCoords(operation);
+ const auto [coords, temporary, swizzle] = BuildCoords(operation);
std::string comp;
if (!meta.sampler.is_shadow) {
@@ -1892,7 +1916,7 @@ std::string ARBDecompiler::TextureGather(Operation operation) {
AddLine("TXG.F {}, {}, texture[{}]{}, {}{};", temporary, temporary, sampler_id, comp,
TextureType(meta), BuildAoffi(operation));
- AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
+ AddLine("MOV.U {}.x, {}.{};", temporary, coords, Swizzle(meta.element));
return fmt::format("{}.x", temporary);
}
@@ -1930,13 +1954,13 @@ std::string ARBDecompiler::TextureQueryLod(Operation operation) {
std::string ARBDecompiler::TexelFetch(Operation operation) {
const auto& meta = std::get<MetaTexture>(operation.GetMeta());
const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
- const auto [temporary, swizzle] = BuildCoords(operation);
+ const auto [coords, temporary, swizzle] = BuildCoords(operation);
if (!meta.sampler.is_buffer) {
ASSERT(swizzle < 4);
AddLine("MOV.F {}.w, {};", temporary, Visit(meta.lod));
}
- AddLine("TXF.F {}, {}, texture[{}], {}{};", temporary, temporary, sampler_id, TextureType(meta),
+ AddLine("TXF.F {}, {}, texture[{}], {}{};", temporary, coords, sampler_id, TextureType(meta),
BuildAoffi(operation));
AddLine("MOV.U {}.x, {}.{};", temporary, temporary, Swizzle(meta.element));
return fmt::format("{}.x", temporary);
@@ -1947,7 +1971,7 @@ std::string ARBDecompiler::TextureGradient(Operation operation) {
const u32 sampler_id = device.GetBaseBindings(stage).sampler + meta.sampler.index;
const std::string ddx = AllocVectorTemporary();
const std::string ddy = AllocVectorTemporary();
- const std::string coord = BuildCoords(operation).first;
+ const std::string coord = std::get<1>(BuildCoords(operation));
const std::size_t num_components = meta.derivates.size() / 2;
for (std::size_t index = 0; index < num_components; ++index) {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.cpp b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
index e866d8f2f..b1c4cd62f 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.cpp
@@ -59,9 +59,10 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst
static_cast<GLintptr>(dst_offset), static_cast<GLsizeiptr>(size));
}
-OGLBufferCache::OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
+OGLBufferCache::OGLBufferCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
const Device& device_, std::size_t stream_size)
- : GenericBufferCache{rasterizer, system,
+ : GenericBufferCache{rasterizer, gpu_memory, cpu_memory,
std::make_unique<OGLStreamBuffer>(device_, stream_size, true)},
device{device_} {
if (!device.HasFastBufferSubData()) {
diff --git a/src/video_core/renderer_opengl/gl_buffer_cache.h b/src/video_core/renderer_opengl/gl_buffer_cache.h
index 88fdc0536..f75b32e31 100644
--- a/src/video_core/renderer_opengl/gl_buffer_cache.h
+++ b/src/video_core/renderer_opengl/gl_buffer_cache.h
@@ -52,7 +52,8 @@ private:
using GenericBufferCache = VideoCommon::BufferCache<Buffer, GLuint, OGLStreamBuffer>;
class OGLBufferCache final : public GenericBufferCache {
public:
- explicit OGLBufferCache(RasterizerOpenGL& rasterizer, Core::System& system,
+ explicit OGLBufferCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
const Device& device, std::size_t stream_size);
~OGLBufferCache();
diff --git a/src/video_core/renderer_opengl/gl_device.cpp b/src/video_core/renderer_opengl/gl_device.cpp
index e7d95149f..a94e4f72e 100644
--- a/src/video_core/renderer_opengl/gl_device.cpp
+++ b/src/video_core/renderer_opengl/gl_device.cpp
@@ -193,7 +193,6 @@ bool IsASTCSupported() {
Device::Device()
: max_uniform_buffers{BuildMaxUniformBuffers()}, base_bindings{BuildBaseBindings()} {
const std::string_view vendor = reinterpret_cast<const char*>(glGetString(GL_VENDOR));
- const std::string_view renderer = reinterpret_cast<const char*>(glGetString(GL_RENDERER));
const std::string_view version = reinterpret_cast<const char*>(glGetString(GL_VERSION));
const std::vector extensions = GetExtensions();
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.cpp b/src/video_core/renderer_opengl/gl_fence_manager.cpp
index 3d2588dd2..b532fdcc2 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_fence_manager.cpp
@@ -45,11 +45,10 @@ void GLInnerFence::Wait() {
glClientWaitSync(sync_object.handle, 0, GL_TIMEOUT_IGNORED);
}
-FenceManagerOpenGL::FenceManagerOpenGL(Core::System& system,
- VideoCore::RasterizerInterface& rasterizer,
+FenceManagerOpenGL::FenceManagerOpenGL(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
TextureCacheOpenGL& texture_cache,
OGLBufferCache& buffer_cache, QueryCache& query_cache)
- : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache) {}
+ : GenericFenceManager{rasterizer, gpu, texture_cache, buffer_cache, query_cache} {}
Fence FenceManagerOpenGL::CreateFence(u32 value, bool is_stubbed) {
return std::make_shared<GLInnerFence>(value, is_stubbed);
diff --git a/src/video_core/renderer_opengl/gl_fence_manager.h b/src/video_core/renderer_opengl/gl_fence_manager.h
index 1686cf5c8..da1dcdace 100644
--- a/src/video_core/renderer_opengl/gl_fence_manager.h
+++ b/src/video_core/renderer_opengl/gl_fence_manager.h
@@ -37,9 +37,9 @@ using GenericFenceManager =
class FenceManagerOpenGL final : public GenericFenceManager {
public:
- FenceManagerOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache,
- QueryCache& query_cache);
+ explicit FenceManagerOpenGL(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
+ TextureCacheOpenGL& texture_cache, OGLBufferCache& buffer_cache,
+ QueryCache& query_cache);
protected:
Fence CreateFence(u32 value, bool is_stubbed) override;
diff --git a/src/video_core/renderer_opengl/gl_query_cache.cpp b/src/video_core/renderer_opengl/gl_query_cache.cpp
index d7ba57aca..1a3d9720e 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_query_cache.cpp
@@ -30,12 +30,11 @@ constexpr GLenum GetTarget(VideoCore::QueryType type) {
} // Anonymous namespace
-QueryCache::QueryCache(Core::System& system, RasterizerOpenGL& gl_rasterizer)
- : VideoCommon::QueryCacheBase<
- QueryCache, CachedQuery, CounterStream, HostCounter,
- std::vector<OGLQuery>>{system,
- static_cast<VideoCore::RasterizerInterface&>(gl_rasterizer)},
- gl_rasterizer{gl_rasterizer} {}
+QueryCache::QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
+ Tegra::MemoryManager& gpu_memory)
+ : VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter>(
+ rasterizer, maxwell3d, gpu_memory),
+ gl_rasterizer{rasterizer} {}
QueryCache::~QueryCache() = default;
@@ -90,6 +89,8 @@ u64 HostCounter::BlockingQuery() const {
CachedQuery::CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr, u8* host_ptr)
: VideoCommon::CachedQueryBase<HostCounter>{cpu_addr, host_ptr}, cache{&cache}, type{type} {}
+CachedQuery::~CachedQuery() = default;
+
CachedQuery::CachedQuery(CachedQuery&& rhs) noexcept
: VideoCommon::CachedQueryBase<HostCounter>(std::move(rhs)), cache{rhs.cache}, type{rhs.type} {}
diff --git a/src/video_core/renderer_opengl/gl_query_cache.h b/src/video_core/renderer_opengl/gl_query_cache.h
index d8e7052a1..82cac51ee 100644
--- a/src/video_core/renderer_opengl/gl_query_cache.h
+++ b/src/video_core/renderer_opengl/gl_query_cache.h
@@ -26,10 +26,11 @@ class RasterizerOpenGL;
using CounterStream = VideoCommon::CounterStreamBase<QueryCache, HostCounter>;
-class QueryCache final : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream,
- HostCounter, std::vector<OGLQuery>> {
+class QueryCache final
+ : public VideoCommon::QueryCacheBase<QueryCache, CachedQuery, CounterStream, HostCounter> {
public:
- explicit QueryCache(Core::System& system, RasterizerOpenGL& rasterizer);
+ explicit QueryCache(RasterizerOpenGL& rasterizer, Tegra::Engines::Maxwell3D& maxwell3d,
+ Tegra::MemoryManager& gpu_memory);
~QueryCache();
OGLQuery AllocateQuery(VideoCore::QueryType type);
@@ -40,6 +41,7 @@ public:
private:
RasterizerOpenGL& gl_rasterizer;
+ std::array<std::vector<OGLQuery>, VideoCore::NumQueryTypes> query_pools;
};
class HostCounter final : public VideoCommon::HostCounterBase<QueryCache, HostCounter> {
@@ -62,10 +64,12 @@ class CachedQuery final : public VideoCommon::CachedQueryBase<HostCounter> {
public:
explicit CachedQuery(QueryCache& cache, VideoCore::QueryType type, VAddr cpu_addr,
u8* host_ptr);
- CachedQuery(CachedQuery&& rhs) noexcept;
- CachedQuery(const CachedQuery&) = delete;
+ ~CachedQuery() override;
+ CachedQuery(CachedQuery&& rhs) noexcept;
CachedQuery& operator=(CachedQuery&& rhs) noexcept;
+
+ CachedQuery(const CachedQuery&) = delete;
CachedQuery& operator=(const CachedQuery&) = delete;
void Flush() override;
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.cpp b/src/video_core/renderer_opengl/gl_rasterizer.cpp
index 4af5824cd..cfddbde5d 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.cpp
+++ b/src/video_core/renderer_opengl/gl_rasterizer.cpp
@@ -139,30 +139,29 @@ void oglEnable(GLenum cap, bool state) {
(state ? glEnable : glDisable)(cap);
}
-void UpdateBindlessPointers(GLenum target, GLuint64EXT* pointers, std::size_t num_entries) {
- if (num_entries == 0) {
+void UpdateBindlessSSBOs(GLenum target, const BindlessSSBO* ssbos, size_t num_ssbos) {
+ if (num_ssbos == 0) {
return;
}
- if (num_entries % 2 == 1) {
- pointers[num_entries] = 0;
- }
- const GLsizei num_vectors = static_cast<GLsizei>((num_entries + 1) / 2);
- glProgramLocalParametersI4uivNV(target, 0, num_vectors,
- reinterpret_cast<const GLuint*>(pointers));
+ glProgramLocalParametersI4uivNV(target, 0, static_cast<GLsizei>(num_ssbos),
+ reinterpret_cast<const GLuint*>(ssbos));
}
} // Anonymous namespace
-RasterizerOpenGL::RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
- const Device& device, ScreenInfo& info,
- ProgramManager& program_manager, StateTracker& state_tracker)
- : RasterizerAccelerated{system.Memory()}, device{device}, texture_cache{system, *this, device,
- state_tracker},
- shader_cache{*this, system, emu_window, device}, query_cache{system, *this},
- buffer_cache{*this, system, device, STREAM_BUFFER_SIZE},
- fence_manager{system, *this, texture_cache, buffer_cache, query_cache}, system{system},
- screen_info{info}, program_manager{program_manager}, state_tracker{state_tracker},
- async_shaders{emu_window} {
+RasterizerOpenGL::RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_,
+ Core::Memory::Memory& cpu_memory, const Device& device_,
+ ScreenInfo& screen_info_, ProgramManager& program_manager_,
+ StateTracker& state_tracker_)
+ : RasterizerAccelerated{cpu_memory}, gpu(gpu_), maxwell3d(gpu.Maxwell3D()),
+ kepler_compute(gpu.KeplerCompute()), gpu_memory(gpu.MemoryManager()), device(device_),
+ screen_info(screen_info_), program_manager(program_manager_), state_tracker(state_tracker_),
+ texture_cache(*this, maxwell3d, gpu_memory, device, state_tracker),
+ shader_cache(*this, emu_window, gpu, maxwell3d, kepler_compute, gpu_memory, device),
+ query_cache(*this, maxwell3d, gpu_memory),
+ buffer_cache(*this, gpu_memory, cpu_memory, device, STREAM_BUFFER_SIZE),
+ fence_manager(*this, gpu, texture_cache, buffer_cache, query_cache),
+ async_shaders(emu_window) {
CheckExtensions();
unified_uniform_buffer.Create();
@@ -196,8 +195,7 @@ void RasterizerOpenGL::CheckExtensions() {
}
void RasterizerOpenGL::SetupVertexFormat() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::VertexFormats]) {
return;
}
@@ -217,7 +215,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
}
flags[Dirty::VertexFormat0 + index] = false;
- const auto attrib = gpu.regs.vertex_attrib_format[index];
+ const auto attrib = maxwell3d.regs.vertex_attrib_format[index];
const auto gl_index = static_cast<GLuint>(index);
// Disable constant attributes.
@@ -241,8 +239,7 @@ void RasterizerOpenGL::SetupVertexFormat() {
}
void RasterizerOpenGL::SetupVertexBuffer() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::VertexBuffers]) {
return;
}
@@ -253,7 +250,7 @@ void RasterizerOpenGL::SetupVertexBuffer() {
const bool use_unified_memory = device.HasVertexBufferUnifiedMemory();
// Upload all guest vertex arrays sequentially to our buffer
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_BINDINGS; ++index) {
if (!flags[Dirty::VertexBuffer0 + index]) {
continue;
@@ -290,14 +287,13 @@ void RasterizerOpenGL::SetupVertexBuffer() {
}
void RasterizerOpenGL::SetupVertexInstances() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::VertexInstances]) {
return;
}
flags[Dirty::VertexInstances] = false;
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
for (std::size_t index = 0; index < NUM_SUPPORTED_VERTEX_ATTRIBUTES; ++index) {
if (!flags[Dirty::VertexInstance0 + index]) {
continue;
@@ -313,7 +309,7 @@ void RasterizerOpenGL::SetupVertexInstances() {
GLintptr RasterizerOpenGL::SetupIndexBuffer() {
MICROPROFILE_SCOPE(OpenGL_Index);
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
const std::size_t size = CalculateIndexBufferSize();
const auto info = buffer_cache.UploadMemory(regs.index_array.IndexStart(), size);
glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, info.handle);
@@ -322,15 +318,14 @@ GLintptr RasterizerOpenGL::SetupIndexBuffer() {
void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
MICROPROFILE_SCOPE(OpenGL_Shader);
- auto& gpu = system.GPU().Maxwell3D();
u32 clip_distances = 0;
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
- const auto& shader_config = gpu.regs.shader_config[index];
+ const auto& shader_config = maxwell3d.regs.shader_config[index];
const auto program{static_cast<Maxwell::ShaderProgram>(index)};
// Skip stages that are not enabled
- if (!gpu.regs.IsShaderConfigEnabled(index)) {
+ if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
switch (program) {
case Maxwell::ShaderProgram::Geometry:
program_manager.UseGeometryShader(0);
@@ -391,11 +386,11 @@ void RasterizerOpenGL::SetupShaders(GLenum primitive_mode) {
}
SyncClipEnabled(clip_distances);
- gpu.dirty.flags[Dirty::Shaders] = false;
+ maxwell3d.dirty.flags[Dirty::Shaders] = false;
}
std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
std::size_t size = 0;
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -413,34 +408,27 @@ std::size_t RasterizerOpenGL::CalculateVertexArraysSize() const {
}
std::size_t RasterizerOpenGL::CalculateIndexBufferSize() const {
- const auto& regs = system.GPU().Maxwell3D().regs;
-
- return static_cast<std::size_t>(regs.index_array.count) *
- static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
+ return static_cast<std::size_t>(maxwell3d.regs.index_array.count) *
+ static_cast<std::size_t>(maxwell3d.regs.index_array.FormatSizeInBytes());
}
-void RasterizerOpenGL::LoadDiskResources(const std::atomic_bool& stop_loading,
+void RasterizerOpenGL::LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
- shader_cache.LoadDiskCache(stop_loading, callback);
-}
-
-void RasterizerOpenGL::SetupDirtyFlags() {
- state_tracker.Initialize();
+ shader_cache.LoadDiskCache(title_id, stop_loading, callback);
}
void RasterizerOpenGL::ConfigureFramebuffers() {
MICROPROFILE_SCOPE(OpenGL_Framebuffer);
- auto& gpu = system.GPU().Maxwell3D();
- if (!gpu.dirty.flags[VideoCommon::Dirty::RenderTargets]) {
+ if (!maxwell3d.dirty.flags[VideoCommon::Dirty::RenderTargets]) {
return;
}
- gpu.dirty.flags[VideoCommon::Dirty::RenderTargets] = false;
+ maxwell3d.dirty.flags[VideoCommon::Dirty::RenderTargets] = false;
texture_cache.GuardRenderTargets(true);
View depth_surface = texture_cache.GetDepthBufferSurface(true);
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
UNIMPLEMENTED_IF(regs.rt_separate_frag_data == 0);
// Bind the framebuffer surfaces
@@ -472,8 +460,7 @@ void RasterizerOpenGL::ConfigureFramebuffers() {
}
void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_depth_stencil) {
- auto& gpu = system.GPU().Maxwell3D();
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
texture_cache.GuardRenderTargets(true);
View color_surface;
@@ -523,12 +510,11 @@ void RasterizerOpenGL::ConfigureClearFramebuffer(bool using_color, bool using_de
}
void RasterizerOpenGL::Clear() {
- const auto& gpu = system.GPU().Maxwell3D();
- if (!gpu.ShouldExecute()) {
+ if (!maxwell3d.ShouldExecute()) {
return;
}
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
bool use_color{};
bool use_depth{};
bool use_stencil{};
@@ -593,7 +579,6 @@ void RasterizerOpenGL::Clear() {
void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
MICROPROFILE_SCOPE(OpenGL_Drawing);
- auto& gpu = system.GPU().Maxwell3D();
query_cache.UpdateCounters();
@@ -641,7 +626,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
if (invalidated) {
// When the stream buffer has been invalidated, we have to consider vertex buffers as dirty
- auto& dirty = gpu.dirty.flags;
+ auto& dirty = maxwell3d.dirty.flags;
dirty[Dirty::VertexBuffers] = true;
for (int index = Dirty::VertexBuffer0; index <= Dirty::VertexBuffer31; ++index) {
dirty[index] = true;
@@ -662,7 +647,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
// Setup emulation uniform buffer.
if (!device.UseAssemblyShaders()) {
MaxwellUniformData ubo;
- ubo.SetFromRegs(gpu);
+ ubo.SetFromRegs(maxwell3d);
const auto info =
buffer_cache.UploadHostMemory(&ubo, sizeof(ubo), device.GetUniformBufferAlignment());
glBindBufferRange(GL_UNIFORM_BUFFER, EmulationUniformBlockBinding, info.handle, info.offset,
@@ -671,7 +656,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
// Setup shaders and their used resources.
texture_cache.GuardSamplers(true);
- const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(gpu.regs.draw.topology);
+ const GLenum primitive_mode = MaxwellToGL::PrimitiveTopology(maxwell3d.regs.draw.topology);
SetupShaders(primitive_mode);
texture_cache.GuardSamplers(false);
@@ -688,14 +673,14 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
BeginTransformFeedback(primitive_mode);
- const GLuint base_instance = static_cast<GLuint>(gpu.regs.vb_base_instance);
+ const GLuint base_instance = static_cast<GLuint>(maxwell3d.regs.vb_base_instance);
const GLsizei num_instances =
- static_cast<GLsizei>(is_instanced ? gpu.mme_draw.instance_count : 1);
+ static_cast<GLsizei>(is_instanced ? maxwell3d.mme_draw.instance_count : 1);
if (is_indexed) {
- const GLint base_vertex = static_cast<GLint>(gpu.regs.vb_element_base);
- const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.index_array.count);
+ const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vb_element_base);
+ const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.index_array.count);
const GLvoid* offset = reinterpret_cast<const GLvoid*>(index_buffer_offset);
- const GLenum format = MaxwellToGL::IndexFormat(gpu.regs.index_array.format);
+ const GLenum format = MaxwellToGL::IndexFormat(maxwell3d.regs.index_array.format);
if (num_instances == 1 && base_instance == 0 && base_vertex == 0) {
glDrawElements(primitive_mode, num_vertices, format, offset);
} else if (num_instances == 1 && base_instance == 0) {
@@ -714,8 +699,8 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
base_instance);
}
} else {
- const GLint base_vertex = static_cast<GLint>(gpu.regs.vertex_buffer.first);
- const GLsizei num_vertices = static_cast<GLsizei>(gpu.regs.vertex_buffer.count);
+ const GLint base_vertex = static_cast<GLint>(maxwell3d.regs.vertex_buffer.first);
+ const GLsizei num_vertices = static_cast<GLsizei>(maxwell3d.regs.vertex_buffer.count);
if (num_instances == 1 && base_instance == 0) {
glDrawArrays(primitive_mode, base_vertex, num_vertices);
} else if (base_instance == 0) {
@@ -730,7 +715,7 @@ void RasterizerOpenGL::Draw(bool is_indexed, bool is_instanced) {
++num_queued_commands;
- system.GPU().TickWork();
+ gpu.TickWork();
}
void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
@@ -753,7 +738,8 @@ void RasterizerOpenGL::DispatchCompute(GPUVAddr code_addr) {
buffer_cache.Unmap();
- const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
+ const auto& launch_desc = kepler_compute.launch_description;
+ program_manager.BindCompute(kernel->GetHandle());
glDispatchCompute(launch_desc.grid_dim_x, launch_desc.grid_dim_y, launch_desc.grid_dim_z);
++num_queued_commands;
}
@@ -815,17 +801,14 @@ void RasterizerOpenGL::SyncGuestHost() {
}
void RasterizerOpenGL::SignalSemaphore(GPUVAddr addr, u32 value) {
- auto& gpu{system.GPU()};
if (!gpu.IsAsync()) {
- auto& memory_manager{gpu.MemoryManager()};
- memory_manager.Write<u32>(addr, value);
+ gpu_memory.Write<u32>(addr, value);
return;
}
fence_manager.SignalSemaphore(addr, value);
}
void RasterizerOpenGL::SignalSyncPoint(u32 value) {
- auto& gpu{system.GPU()};
if (!gpu.IsAsync()) {
gpu.IncrementSyncPoint(value);
return;
@@ -834,7 +817,6 @@ void RasterizerOpenGL::SignalSyncPoint(u32 value) {
}
void RasterizerOpenGL::ReleaseFences() {
- auto& gpu{system.GPU()};
if (!gpu.IsAsync()) {
return;
}
@@ -914,13 +896,13 @@ bool RasterizerOpenGL::AccelerateDisplay(const Tegra::FramebufferConfig& config,
}
void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* shader) {
- static constexpr std::array PARAMETER_LUT = {
- GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
+ static constexpr std::array PARAMETER_LUT{
+ GL_VERTEX_PROGRAM_PARAMETER_BUFFER_NV, GL_TESS_CONTROL_PROGRAM_PARAMETER_BUFFER_NV,
GL_TESS_EVALUATION_PROGRAM_PARAMETER_BUFFER_NV, GL_GEOMETRY_PROGRAM_PARAMETER_BUFFER_NV,
- GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV};
-
+ GL_FRAGMENT_PROGRAM_PARAMETER_BUFFER_NV,
+ };
MICROPROFILE_SCOPE(OpenGL_UBO);
- const auto& stages = system.GPU().Maxwell3D().state.shader_stages;
+ const auto& stages = maxwell3d.state.shader_stages;
const auto& shader_stage = stages[stage_index];
const auto& entries = shader->GetEntries();
const bool use_unified = entries.use_unified_uniforms;
@@ -945,7 +927,7 @@ void RasterizerOpenGL::SetupDrawConstBuffers(std::size_t stage_index, Shader* sh
void RasterizerOpenGL::SetupComputeConstBuffers(Shader* kernel) {
MICROPROFILE_SCOPE(OpenGL_UBO);
- const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
+ const auto& launch_desc = kepler_compute.launch_description;
const auto& entries = kernel->GetEntries();
const bool use_unified = entries.use_unified_uniforms;
@@ -1018,57 +1000,56 @@ void RasterizerOpenGL::SetupDrawGlobalMemory(std::size_t stage_index, Shader* sh
GL_GEOMETRY_PROGRAM_NV, GL_FRAGMENT_PROGRAM_NV,
};
- auto& gpu{system.GPU()};
- auto& memory_manager{gpu.MemoryManager()};
- const auto& cbufs{gpu.Maxwell3D().state.shader_stages[stage_index]};
+ const auto& cbufs{maxwell3d.state.shader_stages[stage_index]};
const auto& entries{shader->GetEntries().global_memory_entries};
- std::array<GLuint64EXT, 32> pointers;
- ASSERT(entries.size() < pointers.size());
+ std::array<BindlessSSBO, 32> ssbos;
+ ASSERT(entries.size() < ssbos.size());
const bool assembly_shaders = device.UseAssemblyShaders();
u32 binding = assembly_shaders ? 0 : device.GetBaseBindings(stage_index).shader_storage_buffer;
for (const auto& entry : entries) {
const GPUVAddr addr{cbufs.const_buffers[entry.cbuf_index].address + entry.cbuf_offset};
- const GPUVAddr gpu_addr{memory_manager.Read<u64>(addr)};
- const u32 size{memory_manager.Read<u32>(addr + 8)};
- SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]);
+ const GPUVAddr gpu_addr{gpu_memory.Read<u64>(addr)};
+ const u32 size{gpu_memory.Read<u32>(addr + 8)};
+ SetupGlobalMemory(binding, entry, gpu_addr, size, &ssbos[binding]);
++binding;
}
if (assembly_shaders) {
- UpdateBindlessPointers(TARGET_LUT[stage_index], pointers.data(), entries.size());
+ UpdateBindlessSSBOs(TARGET_LUT[stage_index], ssbos.data(), entries.size());
}
}
void RasterizerOpenGL::SetupComputeGlobalMemory(Shader* kernel) {
- auto& gpu{system.GPU()};
- auto& memory_manager{gpu.MemoryManager()};
- const auto& cbufs{gpu.KeplerCompute().launch_description.const_buffer_config};
+ const auto& cbufs{kepler_compute.launch_description.const_buffer_config};
const auto& entries{kernel->GetEntries().global_memory_entries};
- std::array<GLuint64EXT, 32> pointers;
- ASSERT(entries.size() < pointers.size());
+ std::array<BindlessSSBO, 32> ssbos;
+ ASSERT(entries.size() < ssbos.size());
u32 binding = 0;
for (const auto& entry : entries) {
const GPUVAddr addr{cbufs[entry.cbuf_index].Address() + entry.cbuf_offset};
- const GPUVAddr gpu_addr{memory_manager.Read<u64>(addr)};
- const u32 size{memory_manager.Read<u32>(addr + 8)};
- SetupGlobalMemory(binding, entry, gpu_addr, size, &pointers[binding]);
+ const GPUVAddr gpu_addr{gpu_memory.Read<u64>(addr)};
+ const u32 size{gpu_memory.Read<u32>(addr + 8)};
+ SetupGlobalMemory(binding, entry, gpu_addr, size, &ssbos[binding]);
++binding;
}
if (device.UseAssemblyShaders()) {
- UpdateBindlessPointers(GL_COMPUTE_PROGRAM_NV, pointers.data(), entries.size());
+ UpdateBindlessSSBOs(GL_COMPUTE_PROGRAM_NV, ssbos.data(), ssbos.size());
}
}
void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry,
- GPUVAddr gpu_addr, std::size_t size,
- GLuint64EXT* pointer) {
- const std::size_t alignment{device.GetShaderStorageBufferAlignment()};
+ GPUVAddr gpu_addr, size_t size, BindlessSSBO* ssbo) {
+ const size_t alignment{device.GetShaderStorageBufferAlignment()};
const auto info = buffer_cache.UploadMemory(gpu_addr, size, alignment, entry.is_written);
if (device.UseAssemblyShaders()) {
- *pointer = info.address + info.offset;
+ *ssbo = BindlessSSBO{
+ .address = static_cast<GLuint64EXT>(info.address + info.offset),
+ .length = static_cast<GLsizei>(size),
+ .padding = 0,
+ };
} else {
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, binding, info.handle, info.offset,
static_cast<GLsizeiptr>(size));
@@ -1077,7 +1058,6 @@ void RasterizerOpenGL::SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& e
void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader) {
MICROPROFILE_SCOPE(OpenGL_Texture);
- const auto& maxwell3d = system.GPU().Maxwell3D();
u32 binding = device.GetBaseBindings(stage_index).sampler;
for (const auto& entry : shader->GetEntries().samplers) {
const auto shader_type = static_cast<ShaderType>(stage_index);
@@ -1090,11 +1070,10 @@ void RasterizerOpenGL::SetupDrawTextures(std::size_t stage_index, Shader* shader
void RasterizerOpenGL::SetupComputeTextures(Shader* kernel) {
MICROPROFILE_SCOPE(OpenGL_Texture);
- const auto& compute = system.GPU().KeplerCompute();
u32 binding = 0;
for (const auto& entry : kernel->GetEntries().samplers) {
for (std::size_t i = 0; i < entry.size; ++i) {
- const auto texture = GetTextureInfo(compute, entry, ShaderType::Compute, i);
+ const auto texture = GetTextureInfo(kepler_compute, entry, ShaderType::Compute, i);
SetupTexture(binding++, texture, entry);
}
}
@@ -1118,20 +1097,18 @@ void RasterizerOpenGL::SetupTexture(u32 binding, const Tegra::Texture::FullTextu
}
void RasterizerOpenGL::SetupDrawImages(std::size_t stage_index, Shader* shader) {
- const auto& maxwell3d = system.GPU().Maxwell3D();
u32 binding = device.GetBaseBindings(stage_index).image;
for (const auto& entry : shader->GetEntries().images) {
- const auto shader_type = static_cast<Tegra::Engines::ShaderType>(stage_index);
+ const auto shader_type = static_cast<ShaderType>(stage_index);
const auto tic = GetTextureInfo(maxwell3d, entry, shader_type).tic;
SetupImage(binding++, tic, entry);
}
}
void RasterizerOpenGL::SetupComputeImages(Shader* shader) {
- const auto& compute = system.GPU().KeplerCompute();
u32 binding = 0;
for (const auto& entry : shader->GetEntries().images) {
- const auto tic = GetTextureInfo(compute, entry, Tegra::Engines::ShaderType::Compute).tic;
+ const auto tic = GetTextureInfo(kepler_compute, entry, ShaderType::Compute).tic;
SetupImage(binding++, tic, entry);
}
}
@@ -1151,9 +1128,8 @@ void RasterizerOpenGL::SetupImage(u32 binding, const Tegra::Texture::TICEntry& t
}
void RasterizerOpenGL::SyncViewport() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
- const auto& regs = gpu.regs;
+ auto& flags = maxwell3d.dirty.flags;
+ const auto& regs = maxwell3d.regs;
const bool dirty_viewport = flags[Dirty::Viewports];
const bool dirty_clip_control = flags[Dirty::ClipControl];
@@ -1180,7 +1156,7 @@ void RasterizerOpenGL::SyncViewport() {
flags[Dirty::ClipControl] = false;
bool flip_y = false;
- if (regs.viewport_transform[0].scale_y < 0.0) {
+ if (regs.viewport_transform[0].scale_y < 0.0f) {
flip_y = !flip_y;
}
if (regs.screen_y_control.y_negate != 0) {
@@ -1225,25 +1201,23 @@ void RasterizerOpenGL::SyncViewport() {
}
void RasterizerOpenGL::SyncDepthClamp() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::DepthClampEnabled]) {
return;
}
flags[Dirty::DepthClampEnabled] = false;
- oglEnable(GL_DEPTH_CLAMP, gpu.regs.view_volume_clip_control.depth_clamp_disabled == 0);
+ oglEnable(GL_DEPTH_CLAMP, maxwell3d.regs.view_volume_clip_control.depth_clamp_disabled == 0);
}
void RasterizerOpenGL::SyncClipEnabled(u32 clip_mask) {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::ClipDistances] && !flags[Dirty::Shaders]) {
return;
}
flags[Dirty::ClipDistances] = false;
- clip_mask &= gpu.regs.clip_distance_enabled;
+ clip_mask &= maxwell3d.regs.clip_distance_enabled;
if (clip_mask == last_clip_distance_mask) {
return;
}
@@ -1259,9 +1233,8 @@ void RasterizerOpenGL::SyncClipCoef() {
}
void RasterizerOpenGL::SyncCullMode() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
- const auto& regs = gpu.regs;
+ auto& flags = maxwell3d.dirty.flags;
+ const auto& regs = maxwell3d.regs;
if (flags[Dirty::CullTest]) {
flags[Dirty::CullTest] = false;
@@ -1276,26 +1249,24 @@ void RasterizerOpenGL::SyncCullMode() {
}
void RasterizerOpenGL::SyncPrimitiveRestart() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::PrimitiveRestart]) {
return;
}
flags[Dirty::PrimitiveRestart] = false;
- if (gpu.regs.primitive_restart.enabled) {
+ if (maxwell3d.regs.primitive_restart.enabled) {
glEnable(GL_PRIMITIVE_RESTART);
- glPrimitiveRestartIndex(gpu.regs.primitive_restart.index);
+ glPrimitiveRestartIndex(maxwell3d.regs.primitive_restart.index);
} else {
glDisable(GL_PRIMITIVE_RESTART);
}
}
void RasterizerOpenGL::SyncDepthTestState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
+ const auto& regs = maxwell3d.regs;
- const auto& regs = gpu.regs;
if (flags[Dirty::DepthMask]) {
flags[Dirty::DepthMask] = false;
glDepthMask(regs.depth_write_enabled ? GL_TRUE : GL_FALSE);
@@ -1313,14 +1284,13 @@ void RasterizerOpenGL::SyncDepthTestState() {
}
void RasterizerOpenGL::SyncStencilTestState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::StencilTest]) {
return;
}
flags[Dirty::StencilTest] = false;
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
oglEnable(GL_STENCIL_TEST, regs.stencil_enable);
glStencilFuncSeparate(GL_FRONT, MaxwellToGL::ComparisonOp(regs.stencil_front_func_func),
@@ -1345,25 +1315,24 @@ void RasterizerOpenGL::SyncStencilTestState() {
}
void RasterizerOpenGL::SyncRasterizeEnable() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::RasterizeEnable]) {
return;
}
flags[Dirty::RasterizeEnable] = false;
- oglEnable(GL_RASTERIZER_DISCARD, gpu.regs.rasterize_enable == 0);
+ oglEnable(GL_RASTERIZER_DISCARD, maxwell3d.regs.rasterize_enable == 0);
}
void RasterizerOpenGL::SyncPolygonModes() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::PolygonModes]) {
return;
}
flags[Dirty::PolygonModes] = false;
- if (gpu.regs.fill_rectangle) {
+ const auto& regs = maxwell3d.regs;
+ if (regs.fill_rectangle) {
if (!GLAD_GL_NV_fill_rectangle) {
LOG_ERROR(Render_OpenGL, "GL_NV_fill_rectangle used and not supported");
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
@@ -1376,27 +1345,26 @@ void RasterizerOpenGL::SyncPolygonModes() {
return;
}
- if (gpu.regs.polygon_mode_front == gpu.regs.polygon_mode_back) {
+ if (regs.polygon_mode_front == regs.polygon_mode_back) {
flags[Dirty::PolygonModeFront] = false;
flags[Dirty::PolygonModeBack] = false;
- glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front));
+ glPolygonMode(GL_FRONT_AND_BACK, MaxwellToGL::PolygonMode(regs.polygon_mode_front));
return;
}
if (flags[Dirty::PolygonModeFront]) {
flags[Dirty::PolygonModeFront] = false;
- glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_front));
+ glPolygonMode(GL_FRONT, MaxwellToGL::PolygonMode(regs.polygon_mode_front));
}
if (flags[Dirty::PolygonModeBack]) {
flags[Dirty::PolygonModeBack] = false;
- glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(gpu.regs.polygon_mode_back));
+ glPolygonMode(GL_BACK, MaxwellToGL::PolygonMode(regs.polygon_mode_back));
}
}
void RasterizerOpenGL::SyncColorMask() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::ColorMasks]) {
return;
}
@@ -1405,7 +1373,7 @@ void RasterizerOpenGL::SyncColorMask() {
const bool force = flags[Dirty::ColorMaskCommon];
flags[Dirty::ColorMaskCommon] = false;
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
if (regs.color_mask_common) {
if (!force && !flags[Dirty::ColorMask0]) {
return;
@@ -1430,33 +1398,30 @@ void RasterizerOpenGL::SyncColorMask() {
}
void RasterizerOpenGL::SyncMultiSampleState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::MultisampleControl]) {
return;
}
flags[Dirty::MultisampleControl] = false;
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
oglEnable(GL_SAMPLE_ALPHA_TO_COVERAGE, regs.multisample_control.alpha_to_coverage);
oglEnable(GL_SAMPLE_ALPHA_TO_ONE, regs.multisample_control.alpha_to_one);
}
void RasterizerOpenGL::SyncFragmentColorClampState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::FragmentClampColor]) {
return;
}
flags[Dirty::FragmentClampColor] = false;
- glClampColor(GL_CLAMP_FRAGMENT_COLOR, gpu.regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
+ glClampColor(GL_CLAMP_FRAGMENT_COLOR, maxwell3d.regs.frag_color_clamp ? GL_TRUE : GL_FALSE);
}
void RasterizerOpenGL::SyncBlendState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
- const auto& regs = gpu.regs;
+ auto& flags = maxwell3d.dirty.flags;
+ const auto& regs = maxwell3d.regs;
if (flags[Dirty::BlendColor]) {
flags[Dirty::BlendColor] = false;
@@ -1513,14 +1478,13 @@ void RasterizerOpenGL::SyncBlendState() {
}
void RasterizerOpenGL::SyncLogicOpState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::LogicOp]) {
return;
}
flags[Dirty::LogicOp] = false;
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
if (regs.logic_op.enable) {
glEnable(GL_COLOR_LOGIC_OP);
glLogicOp(MaxwellToGL::LogicOp(regs.logic_op.operation));
@@ -1530,14 +1494,13 @@ void RasterizerOpenGL::SyncLogicOpState() {
}
void RasterizerOpenGL::SyncScissorTest() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::Scissors]) {
return;
}
flags[Dirty::Scissors] = false;
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
for (std::size_t index = 0; index < Maxwell::NumViewports; ++index) {
if (!flags[Dirty::Scissor0 + index]) {
continue;
@@ -1556,16 +1519,15 @@ void RasterizerOpenGL::SyncScissorTest() {
}
void RasterizerOpenGL::SyncPointState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::PointSize]) {
return;
}
flags[Dirty::PointSize] = false;
- oglEnable(GL_POINT_SPRITE, gpu.regs.point_sprite_enable);
+ oglEnable(GL_POINT_SPRITE, maxwell3d.regs.point_sprite_enable);
- if (gpu.regs.vp_point_size.enable) {
+ if (maxwell3d.regs.vp_point_size.enable) {
// By definition of GL_POINT_SIZE, it only matters if GL_PROGRAM_POINT_SIZE is disabled.
glEnable(GL_PROGRAM_POINT_SIZE);
return;
@@ -1573,32 +1535,30 @@ void RasterizerOpenGL::SyncPointState() {
// Limit the point size to 1 since nouveau sometimes sets a point size of 0 (and that's invalid
// in OpenGL).
- glPointSize(std::max(1.0f, gpu.regs.point_size));
+ glPointSize(std::max(1.0f, maxwell3d.regs.point_size));
glDisable(GL_PROGRAM_POINT_SIZE);
}
void RasterizerOpenGL::SyncLineState() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::LineWidth]) {
return;
}
flags[Dirty::LineWidth] = false;
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
oglEnable(GL_LINE_SMOOTH, regs.line_smooth_enable);
glLineWidth(regs.line_smooth_enable ? regs.line_width_smooth : regs.line_width_aliased);
}
void RasterizerOpenGL::SyncPolygonOffset() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::PolygonOffset]) {
return;
}
flags[Dirty::PolygonOffset] = false;
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
oglEnable(GL_POLYGON_OFFSET_FILL, regs.polygon_offset_fill_enable);
oglEnable(GL_POLYGON_OFFSET_LINE, regs.polygon_offset_line_enable);
oglEnable(GL_POLYGON_OFFSET_POINT, regs.polygon_offset_point_enable);
@@ -1612,18 +1572,13 @@ void RasterizerOpenGL::SyncPolygonOffset() {
}
void RasterizerOpenGL::SyncAlphaTest() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::AlphaTest]) {
return;
}
flags[Dirty::AlphaTest] = false;
- const auto& regs = gpu.regs;
- if (regs.alpha_test_enabled && regs.rt_control.count > 1) {
- LOG_WARNING(Render_OpenGL, "Alpha testing with more than one render target is not tested");
- }
-
+ const auto& regs = maxwell3d.regs;
if (regs.alpha_test_enabled) {
glEnable(GL_ALPHA_TEST);
glAlphaFunc(MaxwellToGL::ComparisonOp(regs.alpha_test_func), regs.alpha_test_ref);
@@ -1633,20 +1588,19 @@ void RasterizerOpenGL::SyncAlphaTest() {
}
void RasterizerOpenGL::SyncFramebufferSRGB() {
- auto& gpu = system.GPU().Maxwell3D();
- auto& flags = gpu.dirty.flags;
+ auto& flags = maxwell3d.dirty.flags;
if (!flags[Dirty::FramebufferSRGB]) {
return;
}
flags[Dirty::FramebufferSRGB] = false;
- oglEnable(GL_FRAMEBUFFER_SRGB, gpu.regs.framebuffer_srgb);
+ oglEnable(GL_FRAMEBUFFER_SRGB, maxwell3d.regs.framebuffer_srgb);
}
void RasterizerOpenGL::SyncTransformFeedback() {
// TODO(Rodrigo): Inject SKIP_COMPONENTS*_NV when required. An unimplemented message will signal
// when this is required.
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
static constexpr std::size_t STRIDE = 3;
std::array<GLint, 128 * STRIDE * Maxwell::NumTransformFeedbackBuffers> attribs;
@@ -1698,7 +1652,7 @@ void RasterizerOpenGL::SyncTransformFeedback() {
}
void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
if (regs.tfb_enabled == 0) {
return;
}
@@ -1741,7 +1695,7 @@ void RasterizerOpenGL::BeginTransformFeedback(GLenum primitive_mode) {
}
void RasterizerOpenGL::EndTransformFeedback() {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
if (regs.tfb_enabled == 0) {
return;
}
diff --git a/src/video_core/renderer_opengl/gl_rasterizer.h b/src/video_core/renderer_opengl/gl_rasterizer.h
index ccc6f50f6..1d0f585fa 100644
--- a/src/video_core/renderer_opengl/gl_rasterizer.h
+++ b/src/video_core/renderer_opengl/gl_rasterizer.h
@@ -36,8 +36,8 @@
#include "video_core/shader/async_shaders.h"
#include "video_core/textures/texture.h"
-namespace Core {
-class System;
+namespace Core::Memory {
+class Memory;
}
namespace Core::Frontend {
@@ -53,11 +53,19 @@ namespace OpenGL {
struct ScreenInfo;
struct DrawParameters;
+struct BindlessSSBO {
+ GLuint64EXT address;
+ GLsizei length;
+ GLsizei padding;
+};
+static_assert(sizeof(BindlessSSBO) * CHAR_BIT == 128);
+
class RasterizerOpenGL : public VideoCore::RasterizerAccelerated {
public:
- explicit RasterizerOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
- const Device& device, ScreenInfo& info,
- ProgramManager& program_manager, StateTracker& state_tracker);
+ explicit RasterizerOpenGL(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
+ Core::Memory::Memory& cpu_memory, const Device& device,
+ ScreenInfo& screen_info, ProgramManager& program_manager,
+ StateTracker& state_tracker);
~RasterizerOpenGL() override;
void Draw(bool is_indexed, bool is_instanced) override;
@@ -83,9 +91,8 @@ public:
const Tegra::Engines::Fermi2D::Config& copy_config) override;
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
u32 pixel_stride) override;
- void LoadDiskResources(const std::atomic_bool& stop_loading,
+ void LoadDiskResources(u64 title_id, const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) override;
- void SetupDirtyFlags() override;
/// Returns true when there are commands queued to the OpenGL server.
bool AnyCommandQueued() const {
@@ -126,7 +133,7 @@ private:
/// Configures a global memory buffer.
void SetupGlobalMemory(u32 binding, const GlobalMemoryEntry& entry, GPUVAddr gpu_addr,
- std::size_t size, GLuint64EXT* pointer);
+ size_t size, BindlessSSBO* ssbo);
/// Configures the current textures to use for the draw command.
void SetupDrawTextures(std::size_t stage_index, Shader* shader);
@@ -237,7 +244,15 @@ private:
void SetupShaders(GLenum primitive_mode);
+ Tegra::GPU& gpu;
+ Tegra::Engines::Maxwell3D& maxwell3d;
+ Tegra::Engines::KeplerCompute& kepler_compute;
+ Tegra::MemoryManager& gpu_memory;
+
const Device& device;
+ ScreenInfo& screen_info;
+ ProgramManager& program_manager;
+ StateTracker& state_tracker;
TextureCacheOpenGL texture_cache;
ShaderCacheOpenGL shader_cache;
@@ -247,10 +262,6 @@ private:
OGLBufferCache buffer_cache;
FenceManagerOpenGL fence_manager;
- Core::System& system;
- ScreenInfo& screen_info;
- ProgramManager& program_manager;
- StateTracker& state_tracker;
VideoCommon::Shader::AsyncShaders async_shaders;
static constexpr std::size_t STREAM_BUFFER_SIZE = 128 * 1024 * 1024;
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.cpp b/src/video_core/renderer_opengl/gl_resource_manager.cpp
index a787e27d2..0ebcec427 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.cpp
+++ b/src/video_core/renderer_opengl/gl_resource_manager.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <string_view>
#include <utility>
#include <glad/glad.h>
#include "common/common_types.h"
@@ -82,11 +83,13 @@ void OGLSampler::Release() {
handle = 0;
}
-void OGLShader::Create(const char* source, GLenum type) {
- if (handle != 0)
+void OGLShader::Create(std::string_view source, GLenum type) {
+ if (handle != 0) {
return;
- if (source == nullptr)
+ }
+ if (source.empty()) {
return;
+ }
MICROPROFILE_SCOPE(OpenGL_ResourceCreation);
handle = GLShader::LoadShader(source, type);
diff --git a/src/video_core/renderer_opengl/gl_resource_manager.h b/src/video_core/renderer_opengl/gl_resource_manager.h
index b05cb641c..f48398669 100644
--- a/src/video_core/renderer_opengl/gl_resource_manager.h
+++ b/src/video_core/renderer_opengl/gl_resource_manager.h
@@ -4,6 +4,7 @@
#pragma once
+#include <string_view>
#include <utility>
#include <glad/glad.h>
#include "common/common_types.h"
@@ -127,7 +128,7 @@ public:
return *this;
}
- void Create(const char* source, GLenum type);
+ void Create(std::string_view source, GLenum type);
void Release();
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.cpp b/src/video_core/renderer_opengl/gl_shader_cache.cpp
index eb49a36bf..bd56bed0c 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_cache.cpp
@@ -22,6 +22,7 @@
#include "video_core/memory_manager.h"
#include "video_core/renderer_opengl/gl_arb_decompiler.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
+#include "video_core/renderer_opengl/gl_resource_manager.h"
#include "video_core/renderer_opengl/gl_shader_cache.h"
#include "video_core/renderer_opengl/gl_shader_decompiler.h"
#include "video_core/renderer_opengl/gl_shader_disk_cache.h"
@@ -238,12 +239,11 @@ std::unique_ptr<Shader> Shader::CreateStageFromMemory(
ProgramCode code_b, VideoCommon::Shader::AsyncShaders& async_shaders, VAddr cpu_addr) {
const auto shader_type = GetShaderType(program_type);
- auto& gpu = params.system.GPU();
+ auto& gpu = params.gpu;
gpu.ShaderNotify().MarkSharderBuilding();
auto registry = std::make_shared<Registry>(shader_type, gpu.Maxwell3D());
- if (!async_shaders.IsShaderAsync(params.system.GPU()) ||
- !params.device.UseAsynchronousShaders()) {
+ if (!async_shaders.IsShaderAsync(gpu) || !params.device.UseAsynchronousShaders()) {
const ShaderIR ir(code, STAGE_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
// TODO(Rodrigo): Handle VertexA shaders
// std::optional<ShaderIR> ir_b;
@@ -286,11 +286,10 @@ std::unique_ptr<Shader> Shader::CreateStageFromMemory(
std::unique_ptr<Shader> Shader::CreateKernelFromMemory(const ShaderParameters& params,
ProgramCode code) {
- auto& gpu = params.system.GPU();
+ auto& gpu = params.gpu;
gpu.ShaderNotify().MarkSharderBuilding();
- auto& engine = gpu.KeplerCompute();
- auto registry = std::make_shared<Registry>(ShaderType::Compute, engine);
+ auto registry = std::make_shared<Registry>(ShaderType::Compute, params.engine);
const ShaderIR ir(code, KERNEL_MAIN_OFFSET, COMPILER_SETTINGS, *registry);
const u64 uid = params.unique_identifier;
auto program = BuildShader(params.device, ShaderType::Compute, uid, ir, *registry);
@@ -319,15 +318,20 @@ std::unique_ptr<Shader> Shader::CreateFromCache(const ShaderParameters& params,
precompiled_shader.registry, precompiled_shader.entries, precompiled_shader.program));
}
-ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
- Core::Frontend::EmuWindow& emu_window, const Device& device)
- : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system},
- emu_window{emu_window}, device{device}, disk_cache{system} {}
+ShaderCacheOpenGL::ShaderCacheOpenGL(RasterizerOpenGL& rasterizer,
+ Core::Frontend::EmuWindow& emu_window_, Tegra::GPU& gpu_,
+ Tegra::Engines::Maxwell3D& maxwell3d_,
+ Tegra::Engines::KeplerCompute& kepler_compute_,
+ Tegra::MemoryManager& gpu_memory_, const Device& device_)
+ : VideoCommon::ShaderCache<Shader>{rasterizer}, emu_window{emu_window_}, gpu{gpu_},
+ gpu_memory{gpu_memory_}, maxwell3d{maxwell3d_},
+ kepler_compute{kepler_compute_}, device{device_} {}
ShaderCacheOpenGL::~ShaderCacheOpenGL() = default;
-void ShaderCacheOpenGL::LoadDiskCache(const std::atomic_bool& stop_loading,
+void ShaderCacheOpenGL::LoadDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback) {
+ disk_cache.BindTitleID(title_id);
const std::optional transferable = disk_cache.LoadTransferable();
if (!transferable) {
return;
@@ -480,21 +484,19 @@ ProgramSharedPtr ShaderCacheOpenGL::GeneratePrecompiledProgram(
Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
VideoCommon::Shader::AsyncShaders& async_shaders) {
- if (!system.GPU().Maxwell3D().dirty.flags[Dirty::Shaders]) {
+ if (!maxwell3d.dirty.flags[Dirty::Shaders]) {
auto* last_shader = last_shaders[static_cast<std::size_t>(program)];
if (last_shader->IsBuilt()) {
return last_shader;
}
}
- auto& memory_manager{system.GPU().MemoryManager()};
- const GPUVAddr address{GetShaderAddress(system, program)};
+ const GPUVAddr address{GetShaderAddress(maxwell3d, program)};
if (device.UseAsynchronousShaders() && async_shaders.HasCompletedWork()) {
auto completed_work = async_shaders.GetCompletedWork();
for (auto& work : completed_work) {
Shader* shader = TryGet(work.cpu_address);
- auto& gpu = system.GPU();
gpu.ShaderNotify().MarkShaderComplete();
if (shader == nullptr) {
continue;
@@ -506,14 +508,13 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
shader->AsyncGLASMBuilt(std::move(work.program.glasm));
}
+ auto& registry = shader->GetRegistry();
+
ShaderDiskCacheEntry entry;
entry.type = work.shader_type;
entry.code = std::move(work.code);
entry.code_b = std::move(work.code_b);
entry.unique_identifier = work.uid;
-
- auto& registry = shader->GetRegistry();
-
entry.bound_buffer = registry.GetBoundBuffer();
entry.graphics_info = registry.GetGraphicsInfo();
entry.keys = registry.GetKeys();
@@ -524,28 +525,28 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
}
// Look up shader in the cache based on address
- const auto cpu_addr{memory_manager.GpuToCpuAddress(address)};
+ const std::optional<VAddr> cpu_addr{gpu_memory.GpuToCpuAddress(address)};
if (Shader* const shader{cpu_addr ? TryGet(*cpu_addr) : null_shader.get()}) {
return last_shaders[static_cast<std::size_t>(program)] = shader;
}
- const auto host_ptr{memory_manager.GetPointer(address)};
+ const u8* const host_ptr{gpu_memory.GetPointer(address)};
// No shader found - create a new one
- ProgramCode code{GetShaderCode(memory_manager, address, host_ptr, false)};
+ ProgramCode code{GetShaderCode(gpu_memory, address, host_ptr, false)};
ProgramCode code_b;
if (program == Maxwell::ShaderProgram::VertexA) {
- const GPUVAddr address_b{GetShaderAddress(system, Maxwell::ShaderProgram::VertexB)};
- const u8* host_ptr_b = memory_manager.GetPointer(address_b);
- code_b = GetShaderCode(memory_manager, address_b, host_ptr_b, false);
+ const GPUVAddr address_b{GetShaderAddress(maxwell3d, Maxwell::ShaderProgram::VertexB)};
+ const u8* host_ptr_b = gpu_memory.GetPointer(address_b);
+ code_b = GetShaderCode(gpu_memory, address_b, host_ptr_b, false);
}
const std::size_t code_size = code.size() * sizeof(u64);
const u64 unique_identifier = GetUniqueIdentifier(
GetShaderType(program), program == Maxwell::ShaderProgram::VertexA, code, code_b);
- const ShaderParameters params{system, disk_cache, device,
- *cpu_addr, host_ptr, unique_identifier};
+ const ShaderParameters params{gpu, maxwell3d, disk_cache, device,
+ *cpu_addr, host_ptr, unique_identifier};
std::unique_ptr<Shader> shader;
const auto found = runtime_cache.find(unique_identifier);
@@ -567,21 +568,20 @@ Shader* ShaderCacheOpenGL::GetStageProgram(Maxwell::ShaderProgram program,
}
Shader* ShaderCacheOpenGL::GetComputeKernel(GPUVAddr code_addr) {
- auto& memory_manager{system.GPU().MemoryManager()};
- const auto cpu_addr{memory_manager.GpuToCpuAddress(code_addr)};
+ const std::optional<VAddr> cpu_addr{gpu_memory.GpuToCpuAddress(code_addr)};
if (Shader* const kernel = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get()) {
return kernel;
}
- const auto host_ptr{memory_manager.GetPointer(code_addr)};
// No kernel found, create a new one
- ProgramCode code{GetShaderCode(memory_manager, code_addr, host_ptr, true)};
+ const u8* host_ptr{gpu_memory.GetPointer(code_addr)};
+ ProgramCode code{GetShaderCode(gpu_memory, code_addr, host_ptr, true)};
const std::size_t code_size{code.size() * sizeof(u64)};
const u64 unique_identifier{GetUniqueIdentifier(ShaderType::Compute, false, code)};
- const ShaderParameters params{system, disk_cache, device,
- *cpu_addr, host_ptr, unique_identifier};
+ const ShaderParameters params{gpu, kepler_compute, disk_cache, device,
+ *cpu_addr, host_ptr, unique_identifier};
std::unique_ptr<Shader> kernel;
const auto found = runtime_cache.find(unique_identifier);
diff --git a/src/video_core/renderer_opengl/gl_shader_cache.h b/src/video_core/renderer_opengl/gl_shader_cache.h
index 7528ac686..1708af06a 100644
--- a/src/video_core/renderer_opengl/gl_shader_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_cache.h
@@ -25,8 +25,8 @@
#include "video_core/shader/shader_ir.h"
#include "video_core/shader_cache.h"
-namespace Core {
-class System;
+namespace Tegra {
+class MemoryManager;
}
namespace Core::Frontend {
@@ -57,11 +57,12 @@ struct PrecompiledShader {
};
struct ShaderParameters {
- Core::System& system;
+ Tegra::GPU& gpu;
+ Tegra::Engines::ConstBufferEngineInterface& engine;
ShaderDiskCacheOpenGL& disk_cache;
const Device& device;
VAddr cpu_addr;
- u8* host_ptr;
+ const u8* host_ptr;
u64 unique_identifier;
};
@@ -118,12 +119,14 @@ private:
class ShaderCacheOpenGL final : public VideoCommon::ShaderCache<Shader> {
public:
- explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::System& system,
- Core::Frontend::EmuWindow& emu_window, const Device& device);
+ explicit ShaderCacheOpenGL(RasterizerOpenGL& rasterizer, Core::Frontend::EmuWindow& emu_window,
+ Tegra::GPU& gpu, Tegra::Engines::Maxwell3D& maxwell3d,
+ Tegra::Engines::KeplerCompute& kepler_compute,
+ Tegra::MemoryManager& gpu_memory, const Device& device);
~ShaderCacheOpenGL() override;
/// Loads disk cache for the current game
- void LoadDiskCache(const std::atomic_bool& stop_loading,
+ void LoadDiskCache(u64 title_id, const std::atomic_bool& stop_loading,
const VideoCore::DiskResourceLoadCallback& callback);
/// Gets the current specified shader stage program
@@ -138,9 +141,13 @@ private:
const ShaderDiskCacheEntry& entry, const ShaderDiskCachePrecompiled& precompiled_entry,
const std::unordered_set<GLenum>& supported_formats);
- Core::System& system;
Core::Frontend::EmuWindow& emu_window;
+ Tegra::GPU& gpu;
+ Tegra::MemoryManager& gpu_memory;
+ Tegra::Engines::Maxwell3D& maxwell3d;
+ Tegra::Engines::KeplerCompute& kepler_compute;
const Device& device;
+
ShaderDiskCacheOpenGL disk_cache;
std::unordered_map<u64, PrecompiledShader> runtime_cache;
diff --git a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
index 3f75fcd2b..95ca96c8e 100644
--- a/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_decompiler.cpp
@@ -813,7 +813,7 @@ private:
const u8 location = static_cast<u8>(static_cast<u32>(index) * 4 + element);
const auto it = transform_feedback.find(location);
if (it == transform_feedback.end()) {
- return {};
+ return std::nullopt;
}
return it->second.components;
}
@@ -1295,21 +1295,21 @@ private:
switch (element) {
case 0:
UNIMPLEMENTED();
- return {};
+ return std::nullopt;
case 1:
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
- return {};
+ return std::nullopt;
}
return {{"gl_Layer", Type::Int}};
case 2:
if (stage == ShaderType::Vertex && !device.HasVertexViewportLayer()) {
- return {};
+ return std::nullopt;
}
return {{"gl_ViewportIndex", Type::Int}};
case 3:
return {{"gl_PointSize", Type::Float}};
}
- return {};
+ return std::nullopt;
case Attribute::Index::FrontColor:
return {{"gl_FrontColor"s + GetSwizzle(element), Type::Float}};
case Attribute::Index::FrontSecondaryColor:
@@ -1332,7 +1332,7 @@ private:
Type::Float}};
}
UNIMPLEMENTED_MSG("Unhandled output attribute: {}", static_cast<u32>(attribute));
- return {};
+ return std::nullopt;
}
}
@@ -1443,8 +1443,10 @@ private:
return expr + ", vec2(0.0), vec2(0.0))";
case TextureType::TextureCube:
return expr + ", vec3(0.0), vec3(0.0))";
+ default:
+ UNREACHABLE();
+ break;
}
- UNREACHABLE();
}
for (const auto& variant : extras) {
@@ -2054,15 +2056,19 @@ private:
}
Expression Texture(Operation operation) {
- const auto meta = std::get_if<MetaTexture>(&operation.GetMeta());
- ASSERT(meta);
-
- std::string expr = GenerateTexture(
- operation, "", {TextureOffset{}, TextureArgument{Type::Float, meta->bias}});
- if (meta->sampler.is_shadow) {
- expr = "vec4(" + expr + ')';
+ const auto meta = std::get<MetaTexture>(operation.GetMeta());
+ const bool separate_dc = meta.sampler.type == TextureType::TextureCube &&
+ meta.sampler.is_array && meta.sampler.is_shadow;
+ // TODO: Replace this with an array and make GenerateTexture use C++20 std::span
+ const std::vector<TextureIR> extras{
+ TextureOffset{},
+ TextureArgument{Type::Float, meta.bias},
+ };
+ std::string expr = GenerateTexture(operation, "", extras, separate_dc);
+ if (meta.sampler.is_shadow) {
+ expr = fmt::format("vec4({})", expr);
}
- return {expr + GetSwizzle(meta->element), Type::Float};
+ return {expr + GetSwizzle(meta.element), Type::Float};
}
Expression TextureLod(Operation operation) {
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
index 40c0877c1..70dd0c3c6 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.cpp
@@ -206,13 +206,17 @@ bool ShaderDiskCacheEntry::Save(Common::FS::IOFile& file) const {
flat_bindless_samplers.size();
}
-ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL(Core::System& system) : system{system} {}
+ShaderDiskCacheOpenGL::ShaderDiskCacheOpenGL() = default;
ShaderDiskCacheOpenGL::~ShaderDiskCacheOpenGL() = default;
+void ShaderDiskCacheOpenGL::BindTitleID(u64 title_id_) {
+ title_id = title_id_;
+}
+
std::optional<std::vector<ShaderDiskCacheEntry>> ShaderDiskCacheOpenGL::LoadTransferable() {
// Skip games without title id
- const bool has_title_id = system.CurrentProcess()->GetTitleID() != 0;
+ const bool has_title_id = title_id != 0;
if (!Settings::values.use_disk_shader_cache.GetValue() || !has_title_id) {
return std::nullopt;
}
@@ -313,8 +317,7 @@ std::optional<std::vector<ShaderDiskCachePrecompiled>> ShaderDiskCacheOpenGL::Lo
return std::nullopt;
}
}
-
- return std::move(entries);
+ return entries;
}
void ShaderDiskCacheOpenGL::InvalidateTransferable() {
@@ -474,7 +477,7 @@ std::string ShaderDiskCacheOpenGL::GetBaseDir() const {
}
std::string ShaderDiskCacheOpenGL::GetTitleID() const {
- return fmt::format("{:016X}", system.CurrentProcess()->GetTitleID());
+ return fmt::format("{:016X}", title_id);
}
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_disk_cache.h b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
index db2bb73bc..aef841c1d 100644
--- a/src/video_core/renderer_opengl/gl_shader_disk_cache.h
+++ b/src/video_core/renderer_opengl/gl_shader_disk_cache.h
@@ -21,10 +21,6 @@
#include "video_core/engines/shader_type.h"
#include "video_core/shader/registry.h"
-namespace Core {
-class System;
-}
-
namespace Common::FS {
class IOFile;
}
@@ -70,9 +66,12 @@ struct ShaderDiskCachePrecompiled {
class ShaderDiskCacheOpenGL {
public:
- explicit ShaderDiskCacheOpenGL(Core::System& system);
+ explicit ShaderDiskCacheOpenGL();
~ShaderDiskCacheOpenGL();
+ /// Binds a title ID for all future operations.
+ void BindTitleID(u64 title_id);
+
/// Loads transferable cache. If file has a old version or on failure, it deletes the file.
std::optional<std::vector<ShaderDiskCacheEntry>> LoadTransferable();
@@ -157,8 +156,6 @@ private:
return LoadArrayFromPrecompiled(&object, 1);
}
- Core::System& system;
-
// Stores whole precompiled cache which will be read from or saved to the precompiled chache
// file
FileSys::VectorVfsFile precompiled_cache_virtual_file;
@@ -168,8 +165,11 @@ private:
// Stored transferable shaders
std::unordered_set<u64> stored_transferable;
+ /// Title ID to operate on
+ u64 title_id = 0;
+
// The cache has been loaded at boot
- bool is_usable{};
+ bool is_usable = false;
};
} // namespace OpenGL
diff --git a/src/video_core/renderer_opengl/gl_shader_util.cpp b/src/video_core/renderer_opengl/gl_shader_util.cpp
index 9e74eda0d..4bf0d6090 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.cpp
+++ b/src/video_core/renderer_opengl/gl_shader_util.cpp
@@ -2,6 +2,7 @@
// Licensed under GPLv2 or any later version
// Refer to the license.txt file included.
+#include <string_view>
#include <vector>
#include <glad/glad.h>
#include "common/assert.h"
@@ -11,7 +12,8 @@
namespace OpenGL::GLShader {
namespace {
-const char* GetStageDebugName(GLenum type) {
+
+std::string_view StageDebugName(GLenum type) {
switch (type) {
case GL_VERTEX_SHADER:
return "vertex";
@@ -25,12 +27,17 @@ const char* GetStageDebugName(GLenum type) {
UNIMPLEMENTED();
return "unknown";
}
+
} // Anonymous namespace
-GLuint LoadShader(const char* source, GLenum type) {
- const char* debug_type = GetStageDebugName(type);
+GLuint LoadShader(std::string_view source, GLenum type) {
+ const std::string_view debug_type = StageDebugName(type);
const GLuint shader_id = glCreateShader(type);
- glShaderSource(shader_id, 1, &source, nullptr);
+
+ const GLchar* source_string = source.data();
+ const GLint source_length = static_cast<GLint>(source.size());
+
+ glShaderSource(shader_id, 1, &source_string, &source_length);
LOG_DEBUG(Render_OpenGL, "Compiling {} shader...", debug_type);
glCompileShader(shader_id);
diff --git a/src/video_core/renderer_opengl/gl_shader_util.h b/src/video_core/renderer_opengl/gl_shader_util.h
index 03b7548c2..1b770532e 100644
--- a/src/video_core/renderer_opengl/gl_shader_util.h
+++ b/src/video_core/renderer_opengl/gl_shader_util.h
@@ -38,7 +38,7 @@ void LogShaderSource(T... shaders) {
* @param source String of the GLSL shader program
* @param type Type of the shader (GL_VERTEX_SHADER, GL_GEOMETRY_SHADER or GL_FRAGMENT_SHADER)
*/
-GLuint LoadShader(const char* source, GLenum type);
+GLuint LoadShader(std::string_view source, GLenum type);
/**
* Utility function to create and compile an OpenGL GLSL shader program (vertex + fragment shader)
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.cpp b/src/video_core/renderer_opengl/gl_state_tracker.cpp
index d24fad3de..6bcf831f2 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.cpp
+++ b/src/video_core/renderer_opengl/gl_state_tracker.cpp
@@ -214,10 +214,8 @@ void SetupDirtyMisc(Tables& tables) {
} // Anonymous namespace
-StateTracker::StateTracker(Core::System& system) : system{system} {}
-
-void StateTracker::Initialize() {
- auto& dirty = system.GPU().Maxwell3D().dirty;
+StateTracker::StateTracker(Tegra::GPU& gpu) : flags{gpu.Maxwell3D().dirty.flags} {
+ auto& dirty = gpu.Maxwell3D().dirty;
auto& tables = dirty.tables;
SetupDirtyRenderTargets(tables);
SetupDirtyColorMasks(tables);
diff --git a/src/video_core/renderer_opengl/gl_state_tracker.h b/src/video_core/renderer_opengl/gl_state_tracker.h
index 0f823288e..9d127548f 100644
--- a/src/video_core/renderer_opengl/gl_state_tracker.h
+++ b/src/video_core/renderer_opengl/gl_state_tracker.h
@@ -13,8 +13,8 @@
#include "video_core/dirty_flags.h"
#include "video_core/engines/maxwell_3d.h"
-namespace Core {
-class System;
+namespace Tegra {
+class GPU;
}
namespace OpenGL {
@@ -90,9 +90,7 @@ static_assert(Last <= std::numeric_limits<u8>::max());
class StateTracker {
public:
- explicit StateTracker(Core::System& system);
-
- void Initialize();
+ explicit StateTracker(Tegra::GPU& gpu);
void BindIndexBuffer(GLuint new_index_buffer) {
if (index_buffer == new_index_buffer) {
@@ -103,7 +101,6 @@ public:
}
void NotifyScreenDrawVertexArray() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::VertexFormats] = true;
flags[OpenGL::Dirty::VertexFormat0 + 0] = true;
flags[OpenGL::Dirty::VertexFormat0 + 1] = true;
@@ -117,98 +114,81 @@ public:
}
void NotifyPolygonModes() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::PolygonModes] = true;
flags[OpenGL::Dirty::PolygonModeFront] = true;
flags[OpenGL::Dirty::PolygonModeBack] = true;
}
void NotifyViewport0() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::Viewports] = true;
flags[OpenGL::Dirty::Viewport0] = true;
}
void NotifyScissor0() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::Scissors] = true;
flags[OpenGL::Dirty::Scissor0] = true;
}
void NotifyColorMask0() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::ColorMasks] = true;
flags[OpenGL::Dirty::ColorMask0] = true;
}
void NotifyBlend0() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::BlendStates] = true;
flags[OpenGL::Dirty::BlendState0] = true;
}
void NotifyFramebuffer() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[VideoCommon::Dirty::RenderTargets] = true;
}
void NotifyFrontFace() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::FrontFace] = true;
}
void NotifyCullTest() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::CullTest] = true;
}
void NotifyDepthMask() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::DepthMask] = true;
}
void NotifyDepthTest() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::DepthTest] = true;
}
void NotifyStencilTest() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::StencilTest] = true;
}
void NotifyPolygonOffset() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::PolygonOffset] = true;
}
void NotifyRasterizeEnable() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::RasterizeEnable] = true;
}
void NotifyFramebufferSRGB() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::FramebufferSRGB] = true;
}
void NotifyLogicOp() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::LogicOp] = true;
}
void NotifyClipControl() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::ClipControl] = true;
}
void NotifyAlphaTest() {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
flags[OpenGL::Dirty::AlphaTest] = true;
}
private:
- Core::System& system;
+ Tegra::Engines::Maxwell3D::DirtyState::Flags& flags;
GLuint index_buffer = 0;
};
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.cpp b/src/video_core/renderer_opengl/gl_texture_cache.cpp
index f403f388a..a863ef218 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.cpp
+++ b/src/video_core/renderer_opengl/gl_texture_cache.cpp
@@ -532,10 +532,12 @@ OGLTextureView CachedSurfaceView::CreateTextureView() const {
return texture_view;
}
-TextureCacheOpenGL::TextureCacheOpenGL(Core::System& system,
- VideoCore::RasterizerInterface& rasterizer,
- const Device& device, StateTracker& state_tracker)
- : TextureCacheBase{system, rasterizer, device.HasASTC()}, state_tracker{state_tracker} {
+TextureCacheOpenGL::TextureCacheOpenGL(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::Engines::Maxwell3D& maxwell3d,
+ Tegra::MemoryManager& gpu_memory, const Device& device,
+ StateTracker& state_tracker_)
+ : TextureCacheBase{rasterizer, maxwell3d, gpu_memory, device.HasASTC()}, state_tracker{
+ state_tracker_} {
src_framebuffer.Create();
dst_framebuffer.Create();
}
diff --git a/src/video_core/renderer_opengl/gl_texture_cache.h b/src/video_core/renderer_opengl/gl_texture_cache.h
index de8f18489..7787134fc 100644
--- a/src/video_core/renderer_opengl/gl_texture_cache.h
+++ b/src/video_core/renderer_opengl/gl_texture_cache.h
@@ -129,8 +129,10 @@ private:
class TextureCacheOpenGL final : public TextureCacheBase {
public:
- explicit TextureCacheOpenGL(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- const Device& device, StateTracker& state_tracker);
+ explicit TextureCacheOpenGL(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::Engines::Maxwell3D& maxwell3d,
+ Tegra::MemoryManager& gpu_memory, const Device& device,
+ StateTracker& state_tracker);
~TextureCacheOpenGL();
protected:
diff --git a/src/video_core/renderer_opengl/maxwell_to_gl.h b/src/video_core/renderer_opengl/maxwell_to_gl.h
index fe9bd4b5a..a8be2aa37 100644
--- a/src/video_core/renderer_opengl/maxwell_to_gl.h
+++ b/src/video_core/renderer_opengl/maxwell_to_gl.h
@@ -47,6 +47,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
return GL_UNSIGNED_INT;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return GL_UNSIGNED_INT_2_10_10_10_REV;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::SignedNorm:
@@ -70,6 +72,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
return GL_INT;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return GL_INT_2_10_10_10_REV;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::Float:
@@ -84,6 +88,8 @@ inline GLenum VertexFormat(Maxwell::VertexAttribute attrib) {
case Maxwell::VertexAttribute::Size::Size_32_32_32:
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
return GL_FLOAT;
+ default:
+ break;
}
break;
}
diff --git a/src/video_core/renderer_opengl/renderer_opengl.cpp b/src/video_core/renderer_opengl/renderer_opengl.cpp
index c39663db7..2ccca1993 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.cpp
+++ b/src/video_core/renderer_opengl/renderer_opengl.cpp
@@ -21,6 +21,8 @@
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
+#include "video_core/host_shaders/opengl_present_frag.h"
+#include "video_core/host_shaders/opengl_present_vert.h"
#include "video_core/morton.h"
#include "video_core/renderer_opengl/gl_rasterizer.h"
#include "video_core/renderer_opengl/gl_shader_manager.h"
@@ -30,60 +32,6 @@ namespace OpenGL {
namespace {
-constexpr std::size_t SWAP_CHAIN_SIZE = 3;
-
-struct Frame {
- u32 width{}; /// Width of the frame (to detect resize)
- u32 height{}; /// Height of the frame
- bool color_reloaded{}; /// Texture attachment was recreated (ie: resized)
- OpenGL::OGLRenderbuffer color{}; /// Buffer shared between the render/present FBO
- OpenGL::OGLFramebuffer render{}; /// FBO created on the render thread
- OpenGL::OGLFramebuffer present{}; /// FBO created on the present thread
- GLsync render_fence{}; /// Fence created on the render thread
- GLsync present_fence{}; /// Fence created on the presentation thread
- bool is_srgb{}; /// Framebuffer is sRGB or RGB
-};
-
-constexpr char VERTEX_SHADER[] = R"(
-#version 430 core
-
-out gl_PerVertex {
- vec4 gl_Position;
-};
-
-layout (location = 0) in vec2 vert_position;
-layout (location = 1) in vec2 vert_tex_coord;
-layout (location = 0) out vec2 frag_tex_coord;
-
-// This is a truncated 3x3 matrix for 2D transformations:
-// The upper-left 2x2 submatrix performs scaling/rotation/mirroring.
-// The third column performs translation.
-// The third row could be used for projection, which we don't need in 2D. It hence is assumed to
-// implicitly be [0, 0, 1]
-layout (location = 0) uniform mat3x2 modelview_matrix;
-
-void main() {
- // Multiply input position by the rotscale part of the matrix and then manually translate by
- // the last column. This is equivalent to using a full 3x3 matrix and expanding the vector
- // to `vec3(vert_position.xy, 1.0)`
- gl_Position = vec4(mat2(modelview_matrix) * vert_position + modelview_matrix[2], 0.0, 1.0);
- frag_tex_coord = vert_tex_coord;
-}
-)";
-
-constexpr char FRAGMENT_SHADER[] = R"(
-#version 430 core
-
-layout (location = 0) in vec2 frag_tex_coord;
-layout (location = 0) out vec4 color;
-
-layout (binding = 0) uniform sampler2D color_texture;
-
-void main() {
- color = vec4(texture(color_texture, frag_tex_coord).rgb, 1.0f);
-}
-)";
-
constexpr GLint PositionLocation = 0;
constexpr GLint TexCoordLocation = 1;
constexpr GLint ModelViewMatrixLocation = 0;
@@ -96,24 +44,6 @@ struct ScreenRectVertex {
std::array<GLfloat, 2> tex_coord;
};
-/// Returns true if any debug tool is attached
-bool HasDebugTool() {
- const bool nsight = std::getenv("NVTX_INJECTION64_PATH") || std::getenv("NSIGHT_LAUNCHED");
- if (nsight) {
- return true;
- }
-
- GLint num_extensions;
- glGetIntegerv(GL_NUM_EXTENSIONS, &num_extensions);
- for (GLuint index = 0; index < static_cast<GLuint>(num_extensions); ++index) {
- const auto name = reinterpret_cast<const char*>(glGetStringi(GL_EXTENSIONS, index));
- if (!std::strcmp(name, "GL_EXT_debug_tool")) {
- return true;
- }
- }
- return false;
-}
-
/**
* Defines a 1:1 pixel ortographic projection matrix with (0,0) on the top-left
* corner and (width, height) on the lower-bottom.
@@ -197,133 +127,15 @@ void APIENTRY DebugHandler(GLenum source, GLenum type, GLuint id, GLenum severit
} // Anonymous namespace
-/**
- * For smooth Vsync rendering, we want to always present the latest frame that the core generates,
- * but also make sure that rendering happens at the pace that the frontend dictates. This is a
- * helper class that the renderer uses to sync frames between the render thread and the presentation
- * thread
- */
-class FrameMailbox {
-public:
- std::mutex swap_chain_lock;
- std::condition_variable present_cv;
- std::array<Frame, SWAP_CHAIN_SIZE> swap_chain{};
- std::queue<Frame*> free_queue;
- std::deque<Frame*> present_queue;
- Frame* previous_frame{};
-
- FrameMailbox() {
- for (auto& frame : swap_chain) {
- free_queue.push(&frame);
- }
- }
-
- ~FrameMailbox() {
- // lock the mutex and clear out the present and free_queues and notify any people who are
- // blocked to prevent deadlock on shutdown
- std::scoped_lock lock{swap_chain_lock};
- std::queue<Frame*>().swap(free_queue);
- present_queue.clear();
- present_cv.notify_all();
- }
-
- void ReloadPresentFrame(Frame* frame, u32 height, u32 width) {
- frame->present.Release();
- frame->present.Create();
- GLint previous_draw_fbo{};
- glGetIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &previous_draw_fbo);
- glBindFramebuffer(GL_FRAMEBUFFER, frame->present.handle);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
- frame->color.handle);
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- LOG_CRITICAL(Render_OpenGL, "Failed to recreate present FBO!");
- }
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, previous_draw_fbo);
- frame->color_reloaded = false;
- }
-
- void ReloadRenderFrame(Frame* frame, u32 width, u32 height) {
- // Recreate the color texture attachment
- frame->color.Release();
- frame->color.Create();
- const GLenum internal_format = frame->is_srgb ? GL_SRGB8 : GL_RGB8;
- glNamedRenderbufferStorage(frame->color.handle, internal_format, width, height);
-
- // Recreate the FBO for the render target
- frame->render.Release();
- frame->render.Create();
- glBindFramebuffer(GL_FRAMEBUFFER, frame->render.handle);
- glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER,
- frame->color.handle);
- if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
- LOG_CRITICAL(Render_OpenGL, "Failed to recreate render FBO!");
- }
-
- frame->width = width;
- frame->height = height;
- frame->color_reloaded = true;
- }
-
- Frame* GetRenderFrame() {
- std::unique_lock lock{swap_chain_lock};
-
- // If theres no free frames, we will reuse the oldest render frame
- if (free_queue.empty()) {
- auto frame = present_queue.back();
- present_queue.pop_back();
- return frame;
- }
-
- Frame* frame = free_queue.front();
- free_queue.pop();
- return frame;
- }
-
- void ReleaseRenderFrame(Frame* frame) {
- std::unique_lock lock{swap_chain_lock};
- present_queue.push_front(frame);
- present_cv.notify_one();
- }
-
- Frame* TryGetPresentFrame(int timeout_ms) {
- std::unique_lock lock{swap_chain_lock};
- // wait for new entries in the present_queue
- present_cv.wait_for(lock, std::chrono::milliseconds(timeout_ms),
- [&] { return !present_queue.empty(); });
- if (present_queue.empty()) {
- // timed out waiting for a frame to draw so return the previous frame
- return previous_frame;
- }
-
- // free the previous frame and add it back to the free queue
- if (previous_frame) {
- free_queue.push(previous_frame);
- }
-
- // the newest entries are pushed to the front of the queue
- Frame* frame = present_queue.front();
- present_queue.pop_front();
- // remove all old entries from the present queue and move them back to the free_queue
- for (auto f : present_queue) {
- free_queue.push(f);
- }
- present_queue.clear();
- previous_frame = frame;
- return frame;
- }
-};
-
-RendererOpenGL::RendererOpenGL(Core::System& system_, Core::Frontend::EmuWindow& emu_window_,
- Tegra::GPU& gpu_,
- std::unique_ptr<Core::Frontend::GraphicsContext> context_)
- : RendererBase{emu_window_, std::move(context_)}, system{system_},
- emu_window{emu_window_}, gpu{gpu_}, program_manager{device}, has_debug_tool{HasDebugTool()} {}
+RendererOpenGL::RendererOpenGL(Core::TelemetrySession& telemetry_session_,
+ Core::Frontend::EmuWindow& emu_window_,
+ Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
+ std::unique_ptr<Core::Frontend::GraphicsContext> context)
+ : RendererBase{emu_window_, std::move(context)}, telemetry_session{telemetry_session_},
+ emu_window{emu_window_}, cpu_memory{cpu_memory_}, gpu{gpu_}, program_manager{device} {}
RendererOpenGL::~RendererOpenGL() = default;
-MICROPROFILE_DEFINE(OpenGL_RenderFrame, "OpenGL", "Render Frame", MP_RGB(128, 128, 64));
-MICROPROFILE_DEFINE(OpenGL_WaitPresent, "OpenGL", "Wait For Present", MP_RGB(128, 128, 128));
-
void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
if (!framebuffer) {
return;
@@ -332,79 +144,34 @@ void RendererOpenGL::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
PrepareRendertarget(framebuffer);
RenderScreenshot();
- Frame* frame;
- {
- MICROPROFILE_SCOPE(OpenGL_WaitPresent);
-
- frame = frame_mailbox->GetRenderFrame();
-
- // Clean up sync objects before drawing
+ glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
+ DrawScreen(emu_window.GetFramebufferLayout());
- // INTEL driver workaround. We can't delete the previous render sync object until we are
- // sure that the presentation is done
- if (frame->present_fence) {
- glClientWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
- }
-
- // delete the draw fence if the frame wasn't presented
- if (frame->render_fence) {
- glDeleteSync(frame->render_fence);
- frame->render_fence = 0;
- }
-
- // wait for the presentation to be done
- if (frame->present_fence) {
- glWaitSync(frame->present_fence, 0, GL_TIMEOUT_IGNORED);
- glDeleteSync(frame->present_fence);
- frame->present_fence = 0;
- }
- }
+ ++m_current_frame;
- {
- MICROPROFILE_SCOPE(OpenGL_RenderFrame);
- const auto& layout = render_window.GetFramebufferLayout();
-
- // Recreate the frame if the size of the window has changed
- if (layout.width != frame->width || layout.height != frame->height ||
- screen_info.display_srgb != frame->is_srgb) {
- LOG_DEBUG(Render_OpenGL, "Reloading render frame");
- frame->is_srgb = screen_info.display_srgb;
- frame_mailbox->ReloadRenderFrame(frame, layout.width, layout.height);
- }
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, frame->render.handle);
- DrawScreen(layout);
- // Create a fence for the frontend to wait on and swap this frame to OffTex
- frame->render_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
- glFlush();
- frame_mailbox->ReleaseRenderFrame(frame);
- m_current_frame++;
- rasterizer->TickFrame();
- }
+ rasterizer->TickFrame();
render_window.PollEvents();
- if (has_debug_tool) {
- glBindFramebuffer(GL_DRAW_FRAMEBUFFER, 0);
- Present(0);
- context->SwapBuffers();
- }
+ context->SwapBuffers();
}
void RendererOpenGL::PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer) {
- if (framebuffer) {
- // If framebuffer is provided, reload it from memory to a texture
- if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
- screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
- screen_info.texture.pixel_format != framebuffer->pixel_format ||
- gl_framebuffer_data.empty()) {
- // Reallocate texture if the framebuffer size has changed.
- // This is expected to not happen very often and hence should not be a
- // performance problem.
- ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
- }
-
- // Load the framebuffer from memory, draw it to the screen, and swap buffers
- LoadFBToScreenInfo(*framebuffer);
+ if (!framebuffer) {
+ return;
+ }
+ // If framebuffer is provided, reload it from memory to a texture
+ if (screen_info.texture.width != static_cast<GLsizei>(framebuffer->width) ||
+ screen_info.texture.height != static_cast<GLsizei>(framebuffer->height) ||
+ screen_info.texture.pixel_format != framebuffer->pixel_format ||
+ gl_framebuffer_data.empty()) {
+ // Reallocate texture if the framebuffer size has changed.
+ // This is expected to not happen very often and hence should not be a
+ // performance problem.
+ ConfigureFramebufferTexture(screen_info.texture, *framebuffer);
}
+
+ // Load the framebuffer from memory, draw it to the screen, and swap buffers
+ LoadFBToScreenInfo(*framebuffer);
}
void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuffer) {
@@ -424,7 +191,7 @@ void RendererOpenGL::LoadFBToScreenInfo(const Tegra::FramebufferConfig& framebuf
VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format)};
const u32 bytes_per_pixel{VideoCore::Surface::GetBytesPerPixel(pixel_format)};
const u64 size_in_bytes{framebuffer.stride * framebuffer.height * bytes_per_pixel};
- u8* const host_ptr{system.Memory().GetPointer(framebuffer_addr)};
+ u8* const host_ptr{cpu_memory.GetPointer(framebuffer_addr)};
rasterizer->FlushRegion(ToCacheAddr(host_ptr), size_in_bytes);
// TODO(Rodrigo): Read this from HLE
@@ -454,17 +221,15 @@ void RendererOpenGL::LoadColorToActiveGLTexture(u8 color_r, u8 color_g, u8 color
}
void RendererOpenGL::InitOpenGLObjects() {
- frame_mailbox = std::make_unique<FrameMailbox>();
-
glClearColor(Settings::values.bg_red.GetValue(), Settings::values.bg_green.GetValue(),
Settings::values.bg_blue.GetValue(), 0.0f);
// Create shader programs
OGLShader vertex_shader;
- vertex_shader.Create(VERTEX_SHADER, GL_VERTEX_SHADER);
+ vertex_shader.Create(HostShaders::OPENGL_PRESENT_VERT, GL_VERTEX_SHADER);
OGLShader fragment_shader;
- fragment_shader.Create(FRAGMENT_SHADER, GL_FRAGMENT_SHADER);
+ fragment_shader.Create(HostShaders::OPENGL_PRESENT_FRAG, GL_FRAGMENT_SHADER);
vertex_program.Create(true, false, vertex_shader.handle);
fragment_program.Create(true, false, fragment_shader.handle);
@@ -509,7 +274,6 @@ void RendererOpenGL::AddTelemetryFields() {
LOG_INFO(Render_OpenGL, "GL_VENDOR: {}", gpu_vendor);
LOG_INFO(Render_OpenGL, "GL_RENDERER: {}", gpu_model);
- auto& telemetry_session = system.TelemetrySession();
constexpr auto user_system = Common::Telemetry::FieldType::UserSystem;
telemetry_session.AddField(user_system, "GPU_Vendor", gpu_vendor);
telemetry_session.AddField(user_system, "GPU_Model", gpu_model);
@@ -520,8 +284,8 @@ void RendererOpenGL::CreateRasterizer() {
if (rasterizer) {
return;
}
- rasterizer = std::make_unique<RasterizerOpenGL>(system, emu_window, device, screen_info,
- program_manager, state_tracker);
+ rasterizer = std::make_unique<RasterizerOpenGL>(emu_window, gpu, cpu_memory, device,
+ screen_info, program_manager, state_tracker);
}
void RendererOpenGL::ConfigureFramebufferTexture(TextureInfo& texture,
@@ -684,51 +448,6 @@ void RendererOpenGL::DrawScreen(const Layout::FramebufferLayout& layout) {
program_manager.RestoreGuestPipeline();
}
-bool RendererOpenGL::TryPresent(int timeout_ms) {
- if (has_debug_tool) {
- LOG_DEBUG(Render_OpenGL,
- "Skipping presentation because we are presenting on the main context");
- return false;
- }
- return Present(timeout_ms);
-}
-
-bool RendererOpenGL::Present(int timeout_ms) {
- const auto& layout = render_window.GetFramebufferLayout();
- auto frame = frame_mailbox->TryGetPresentFrame(timeout_ms);
- if (!frame) {
- LOG_DEBUG(Render_OpenGL, "TryGetPresentFrame returned no frame to present");
- return false;
- }
-
- // Clearing before a full overwrite of a fbo can signal to drivers that they can avoid a
- // readback since we won't be doing any blending
- glClear(GL_COLOR_BUFFER_BIT);
-
- // Recreate the presentation FBO if the color attachment was changed
- if (frame->color_reloaded) {
- LOG_DEBUG(Render_OpenGL, "Reloading present frame");
- frame_mailbox->ReloadPresentFrame(frame, layout.width, layout.height);
- }
- glWaitSync(frame->render_fence, 0, GL_TIMEOUT_IGNORED);
- // INTEL workaround.
- // Normally we could just delete the draw fence here, but due to driver bugs, we can just delete
- // it on the emulation thread without too much penalty
- // glDeleteSync(frame.render_sync);
- // frame.render_sync = 0;
-
- glBindFramebuffer(GL_READ_FRAMEBUFFER, frame->present.handle);
- glBlitFramebuffer(0, 0, frame->width, frame->height, 0, 0, layout.width, layout.height,
- GL_COLOR_BUFFER_BIT, GL_LINEAR);
-
- // Insert fence for the main thread to block on
- frame->present_fence = glFenceSync(GL_SYNC_GPU_COMMANDS_COMPLETE, 0);
- glFlush();
-
- glBindFramebuffer(GL_READ_FRAMEBUFFER, 0);
- return true;
-}
-
void RendererOpenGL::RenderScreenshot() {
if (!renderer_settings.screenshot_requested) {
return;
@@ -743,7 +462,7 @@ void RendererOpenGL::RenderScreenshot() {
screenshot_framebuffer.Create();
glBindFramebuffer(GL_FRAMEBUFFER, screenshot_framebuffer.handle);
- Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
+ const Layout::FramebufferLayout layout{renderer_settings.screenshot_framebuffer_layout};
GLuint renderbuffer;
glGenRenderbuffers(1, &renderbuffer);
diff --git a/src/video_core/renderer_opengl/renderer_opengl.h b/src/video_core/renderer_opengl/renderer_opengl.h
index 52ea76b7d..9ef181f95 100644
--- a/src/video_core/renderer_opengl/renderer_opengl.h
+++ b/src/video_core/renderer_opengl/renderer_opengl.h
@@ -16,16 +16,25 @@
namespace Core {
class System;
-}
+class TelemetrySession;
+} // namespace Core
namespace Core::Frontend {
class EmuWindow;
}
+namespace Core::Memory {
+class Memory;
+}
+
namespace Layout {
struct FramebufferLayout;
}
+namespace Tegra {
+class GPU;
+}
+
namespace OpenGL {
/// Structure used for storing information about the textures for the Switch screen
@@ -46,17 +55,10 @@ struct ScreenInfo {
TextureInfo texture;
};
-struct PresentationTexture {
- u32 width = 0;
- u32 height = 0;
- OGLTexture texture;
-};
-
-class FrameMailbox;
-
class RendererOpenGL final : public VideoCore::RendererBase {
public:
- explicit RendererOpenGL(Core::System& system, Core::Frontend::EmuWindow& emu_window,
+ explicit RendererOpenGL(Core::TelemetrySession& telemetry_session,
+ Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
Tegra::GPU& gpu,
std::unique_ptr<Core::Frontend::GraphicsContext> context);
~RendererOpenGL() override;
@@ -64,7 +66,6 @@ public:
bool Init() override;
void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
- bool TryPresent(int timeout_ms) override;
private:
/// Initializes the OpenGL state and creates persistent objects.
@@ -92,14 +93,13 @@ private:
void PrepareRendertarget(const Tegra::FramebufferConfig* framebuffer);
- bool Present(int timeout_ms);
-
- Core::System& system;
+ Core::TelemetrySession& telemetry_session;
Core::Frontend::EmuWindow& emu_window;
+ Core::Memory::Memory& cpu_memory;
Tegra::GPU& gpu;
- const Device device;
- StateTracker state_tracker{system};
+ const Device device;
+ StateTracker state_tracker{gpu};
// OpenGL object IDs
OGLBuffer vertex_buffer;
@@ -123,11 +123,6 @@ private:
/// Used for transforming the framebuffer orientation
Tegra::FramebufferConfig::TransformFlags framebuffer_transform_flags{};
Common::Rectangle<int> framebuffer_crop_rect;
-
- /// Frame presentation mailbox
- std::unique_ptr<FrameMailbox> frame_mailbox;
-
- bool has_debug_tool = false;
};
} // namespace OpenGL
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
index 81a39a3b8..da5c550ea 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.cpp
@@ -58,6 +58,7 @@ void FixedPipelineState::Fill(const Maxwell& regs, bool has_extended_dynamic_sta
logic_op_enable.Assign(regs.logic_op.enable != 0 ? 1 : 0);
logic_op.Assign(PackLogicOp(regs.logic_op.operation));
rasterize_enable.Assign(regs.rasterize_enable != 0 ? 1 : 0);
+ topology.Assign(regs.draw.topology);
std::memcpy(&point_size, &regs.point_size, sizeof(point_size)); // TODO: C++20 std::bit_cast
@@ -131,7 +132,6 @@ void FixedPipelineState::BlendingAttachment::Fill(const Maxwell& regs, std::size
}
void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
- const u32 topology_index = static_cast<u32>(regs.draw.topology.Value());
u32 packed_front_face = PackFrontFace(regs.front_face);
if (regs.screen_y_control.triangle_rast_flip != 0) {
// Flip front face
@@ -161,7 +161,6 @@ void FixedPipelineState::DynamicState::Fill(const Maxwell& regs) {
depth_test_enable.Assign(regs.depth_test_enable);
front_face.Assign(packed_front_face);
depth_test_func.Assign(PackComparisonOp(regs.depth_test_func));
- topology.Assign(topology_index);
cull_face.Assign(PackCullFace(regs.cull_face));
cull_enable.Assign(regs.cull_test_enabled != 0 ? 1 : 0);
diff --git a/src/video_core/renderer_vulkan/fixed_pipeline_state.h b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
index cdcbb65f5..2c18eeaae 100644
--- a/src/video_core/renderer_vulkan/fixed_pipeline_state.h
+++ b/src/video_core/renderer_vulkan/fixed_pipeline_state.h
@@ -150,9 +150,8 @@ struct FixedPipelineState {
};
union {
u32 raw2;
- BitField<0, 4, u32> topology;
- BitField<4, 2, u32> cull_face;
- BitField<6, 1, u32> cull_enable;
+ BitField<0, 2, u32> cull_face;
+ BitField<2, 1, u32> cull_enable;
};
std::array<VertexBinding, Maxwell::NumVertexArrays> vertex_bindings;
@@ -169,10 +168,6 @@ struct FixedPipelineState {
Maxwell::FrontFace FrontFace() const noexcept {
return UnpackFrontFace(front_face.Value());
}
-
- constexpr Maxwell::PrimitiveTopology Topology() const noexcept {
- return static_cast<Maxwell::PrimitiveTopology>(topology.Value());
- }
};
union {
@@ -190,6 +185,7 @@ struct FixedPipelineState {
BitField<18, 1, u32> logic_op_enable;
BitField<19, 4, u32> logic_op;
BitField<23, 1, u32> rasterize_enable;
+ BitField<24, 4, Maxwell::PrimitiveTopology> topology;
};
u32 point_size;
std::array<u32, Maxwell::NumVertexArrays> binding_divisors;
diff --git a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
index f8c77f4fa..d22de1d81 100644
--- a/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
+++ b/src/video_core/renderer_vulkan/maxwell_to_vk.cpp
@@ -78,9 +78,10 @@ VkSamplerAddressMode WrapMode(const VKDevice& device, Tegra::Texture::WrapMode w
case Tegra::Texture::WrapMode::MirrorOnceBorder:
UNIMPLEMENTED();
return VK_SAMPLER_ADDRESS_MODE_MIRROR_CLAMP_TO_EDGE;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
+ return {};
}
- UNIMPLEMENTED_MSG("Unimplemented wrap mode={}", static_cast<u32>(wrap_mode));
- return {};
}
VkCompareOp DepthCompareFunction(Tegra::Texture::DepthCompareFunc depth_compare_func) {
@@ -298,9 +299,10 @@ VkPrimitiveTopology PrimitiveTopology([[maybe_unused]] const VKDevice& device,
return VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;
case Maxwell::PrimitiveTopology::Patches:
return VK_PRIMITIVE_TOPOLOGY_PATCH_LIST;
+ default:
+ UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
+ return {};
}
- UNIMPLEMENTED_MSG("Unimplemented topology={}", static_cast<u32>(topology));
- return {};
}
VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttribute::Size size) {
@@ -325,6 +327,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
return VK_FORMAT_R16G16B16A16_UNORM;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return VK_FORMAT_A2B10G10R10_UNORM_PACK32;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::SignedNorm:
@@ -347,6 +351,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
return VK_FORMAT_R16G16B16A16_SNORM;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return VK_FORMAT_A2B10G10R10_SNORM_PACK32;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::UnsignedScaled:
@@ -369,6 +375,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
return VK_FORMAT_R16G16B16A16_USCALED;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return VK_FORMAT_A2B10G10R10_USCALED_PACK32;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::SignedScaled:
@@ -391,6 +399,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
return VK_FORMAT_R16G16B16A16_SSCALED;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return VK_FORMAT_A2B10G10R10_SSCALED_PACK32;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::UnsignedInt:
@@ -421,6 +431,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
return VK_FORMAT_R32G32B32A32_UINT;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return VK_FORMAT_A2B10G10R10_UINT_PACK32;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::SignedInt:
@@ -451,6 +463,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
return VK_FORMAT_R32G32B32A32_SINT;
case Maxwell::VertexAttribute::Size::Size_10_10_10_2:
return VK_FORMAT_A2B10G10R10_SINT_PACK32;
+ default:
+ break;
}
break;
case Maxwell::VertexAttribute::Type::Float:
@@ -471,6 +485,8 @@ VkFormat VertexFormat(Maxwell::VertexAttribute::Type type, Maxwell::VertexAttrib
return VK_FORMAT_R32G32B32_SFLOAT;
case Maxwell::VertexAttribute::Size::Size_32_32_32_32:
return VK_FORMAT_R32G32B32A32_SFLOAT;
+ default:
+ break;
}
break;
}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.cpp b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
index ae46e0444..f2610868e 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.cpp
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.cpp
@@ -25,9 +25,9 @@
#include "video_core/renderer_vulkan/renderer_vulkan.h"
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -56,7 +56,7 @@ VkBool32 DebugCallback(VkDebugUtilsMessageSeverityFlagBitsEXT severity,
VkDebugUtilsMessageTypeFlagsEXT type,
const VkDebugUtilsMessengerCallbackDataEXT* data,
[[maybe_unused]] void* user_data) {
- const char* message{data->pMessage};
+ const char* const message{data->pMessage};
if (severity & VK_DEBUG_UTILS_MESSAGE_SEVERITY_ERROR_BIT_EXT) {
LOG_CRITICAL(Render_Vulkan, "{}", message);
@@ -86,15 +86,15 @@ Common::DynamicLibrary OpenVulkanLibrary() {
if (!library.Open(filename.c_str())) {
// Android devices may not have libvulkan.so.1, only libvulkan.so.
filename = Common::DynamicLibrary::GetVersionedFilename("vulkan");
- library.Open(filename.c_str());
+ (void)library.Open(filename.c_str());
}
#endif
return library;
}
-vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
- WindowSystemType window_type = WindowSystemType::Headless,
- bool enable_layers = false) {
+std::pair<vk::Instance, u32> CreateInstance(
+ Common::DynamicLibrary& library, vk::InstanceDispatch& dld,
+ WindowSystemType window_type = WindowSystemType::Headless, bool enable_layers = false) {
if (!library.IsOpen()) {
LOG_ERROR(Render_Vulkan, "Vulkan library not available");
return {};
@@ -180,7 +180,10 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc
}
}
- vk::Instance instance = vk::Instance::Create(layers, extensions, dld);
+ // Limit the maximum version of Vulkan to avoid using untested version.
+ const u32 version = std::min(vk::AvailableVersion(dld), static_cast<u32>(VK_API_VERSION_1_1));
+
+ vk::Instance instance = vk::Instance::Create(version, layers, extensions, dld);
if (!instance) {
LOG_ERROR(Render_Vulkan, "Failed to create Vulkan instance");
return {};
@@ -188,7 +191,7 @@ vk::Instance CreateInstance(Common::DynamicLibrary& library, vk::InstanceDispatc
if (!vk::Load(*instance, dld)) {
LOG_ERROR(Render_Vulkan, "Failed to load Vulkan instance function pointers");
}
- return instance;
+ return std::make_pair(std::move(instance), version);
}
std::string GetReadableVersion(u32 version) {
@@ -237,10 +240,12 @@ std::string BuildCommaSeparatedExtensions(std::vector<std::string> available_ext
} // Anonymous namespace
-RendererVulkan::RendererVulkan(Core::System& system_, Core::Frontend::EmuWindow& emu_window,
- Tegra::GPU& gpu_,
+RendererVulkan::RendererVulkan(Core::TelemetrySession& telemetry_session_,
+ Core::Frontend::EmuWindow& emu_window,
+ Core::Memory::Memory& cpu_memory_, Tegra::GPU& gpu_,
std::unique_ptr<Core::Frontend::GraphicsContext> context)
- : RendererBase{emu_window, std::move(context)}, system{system_}, gpu{gpu_} {}
+ : RendererBase{emu_window, std::move(context)}, telemetry_session{telemetry_session_},
+ cpu_memory{cpu_memory_}, gpu{gpu_} {}
RendererVulkan::~RendererVulkan() {
ShutDown();
@@ -267,11 +272,11 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
scheduler->WaitWorker();
swapchain->AcquireNextImage();
- const auto [fence, render_semaphore] = blit_screen->Draw(*framebuffer, use_accelerated);
+ const VkSemaphore render_semaphore = blit_screen->Draw(*framebuffer, use_accelerated);
- scheduler->Flush(false, render_semaphore);
+ scheduler->Flush(render_semaphore);
- if (swapchain->Present(render_semaphore, fence)) {
+ if (swapchain->Present(render_semaphore)) {
blit_screen->Recreate();
}
@@ -281,15 +286,10 @@ void RendererVulkan::SwapBuffers(const Tegra::FramebufferConfig* framebuffer) {
render_window.PollEvents();
}
-bool RendererVulkan::TryPresent(int /*timeout_ms*/) {
- // TODO (bunnei): ImplementMe
- return true;
-}
-
bool RendererVulkan::Init() {
library = OpenVulkanLibrary();
- instance = CreateInstance(library, dld, render_window.GetWindowInfo().type,
- Settings::values.renderer_debug);
+ std::tie(instance, instance_version) = CreateInstance(
+ library, dld, render_window.GetWindowInfo().type, Settings::values.renderer_debug);
if (!instance || !CreateDebugCallback() || !CreateSurface() || !PickDevices()) {
return false;
}
@@ -298,23 +298,21 @@ bool RendererVulkan::Init() {
memory_manager = std::make_unique<VKMemoryManager>(*device);
- resource_manager = std::make_unique<VKResourceManager>(*device);
+ state_tracker = std::make_unique<StateTracker>(gpu);
+
+ scheduler = std::make_unique<VKScheduler>(*device, *state_tracker);
const auto& framebuffer = render_window.GetFramebufferLayout();
- swapchain = std::make_unique<VKSwapchain>(*surface, *device);
+ swapchain = std::make_unique<VKSwapchain>(*surface, *device, *scheduler);
swapchain->Create(framebuffer.width, framebuffer.height, false);
- state_tracker = std::make_unique<StateTracker>(system);
-
- scheduler = std::make_unique<VKScheduler>(*device, *resource_manager, *state_tracker);
-
- rasterizer = std::make_unique<RasterizerVulkan>(system, render_window, screen_info, *device,
- *resource_manager, *memory_manager,
- *state_tracker, *scheduler);
+ rasterizer = std::make_unique<RasterizerVulkan>(render_window, gpu, gpu.MemoryManager(),
+ cpu_memory, screen_info, *device,
+ *memory_manager, *state_tracker, *scheduler);
- blit_screen = std::make_unique<VKBlitScreen>(system, render_window, *rasterizer, *device,
- *resource_manager, *memory_manager, *swapchain,
- *scheduler, screen_info);
+ blit_screen =
+ std::make_unique<VKBlitScreen>(cpu_memory, render_window, *rasterizer, *device,
+ *memory_manager, *swapchain, *scheduler, screen_info);
return true;
}
@@ -332,7 +330,6 @@ void RendererVulkan::ShutDown() {
scheduler.reset();
swapchain.reset();
memory_manager.reset();
- resource_manager.reset();
device.reset();
}
@@ -422,7 +419,8 @@ bool RendererVulkan::PickDevices() {
return false;
}
- device = std::make_unique<VKDevice>(*instance, physical_device, *surface, dld);
+ device =
+ std::make_unique<VKDevice>(*instance, instance_version, physical_device, *surface, dld);
return device->Create();
}
@@ -432,7 +430,7 @@ void RendererVulkan::Report() const {
const std::string driver_version = GetDriverVersion(*device);
const std::string driver_name = fmt::format("{} {}", vendor_name, driver_version);
- const std::string api_version = GetReadableVersion(device->GetApiVersion());
+ const std::string api_version = GetReadableVersion(device->ApiVersion());
const std::string extensions = BuildCommaSeparatedExtensions(device->GetAvailableExtensions());
@@ -440,8 +438,7 @@ void RendererVulkan::Report() const {
LOG_INFO(Render_Vulkan, "Device: {}", model_name);
LOG_INFO(Render_Vulkan, "Vulkan: {}", api_version);
- auto& telemetry_session = system.TelemetrySession();
- constexpr auto field = Common::Telemetry::FieldType::UserSystem;
+ static constexpr auto field = Common::Telemetry::FieldType::UserSystem;
telemetry_session.AddField(field, "GPU_Vendor", vendor_name);
telemetry_session.AddField(field, "GPU_Model", model_name);
telemetry_session.AddField(field, "GPU_Vulkan_Driver", driver_name);
@@ -452,7 +449,7 @@ void RendererVulkan::Report() const {
std::vector<std::string> RendererVulkan::EnumerateDevices() {
vk::InstanceDispatch dld;
Common::DynamicLibrary library = OpenVulkanLibrary();
- vk::Instance instance = CreateInstance(library, dld);
+ vk::Instance instance = CreateInstance(library, dld).first;
if (!instance) {
return {};
}
diff --git a/src/video_core/renderer_vulkan/renderer_vulkan.h b/src/video_core/renderer_vulkan/renderer_vulkan.h
index 13debbbc0..1044ca124 100644
--- a/src/video_core/renderer_vulkan/renderer_vulkan.h
+++ b/src/video_core/renderer_vulkan/renderer_vulkan.h
@@ -14,7 +14,15 @@
#include "video_core/renderer_vulkan/wrapper.h"
namespace Core {
-class System;
+class TelemetrySession;
+}
+
+namespace Core::Memory {
+class Memory;
+}
+
+namespace Tegra {
+class GPU;
}
namespace Vulkan {
@@ -22,9 +30,7 @@ namespace Vulkan {
class StateTracker;
class VKBlitScreen;
class VKDevice;
-class VKFence;
class VKMemoryManager;
-class VKResourceManager;
class VKSwapchain;
class VKScheduler;
class VKImage;
@@ -38,7 +44,8 @@ struct VKScreenInfo {
class RendererVulkan final : public VideoCore::RendererBase {
public:
- explicit RendererVulkan(Core::System& system, Core::Frontend::EmuWindow& emu_window,
+ explicit RendererVulkan(Core::TelemetrySession& telemtry_session,
+ Core::Frontend::EmuWindow& emu_window, Core::Memory::Memory& cpu_memory,
Tegra::GPU& gpu,
std::unique_ptr<Core::Frontend::GraphicsContext> context);
~RendererVulkan() override;
@@ -46,7 +53,6 @@ public:
bool Init() override;
void ShutDown() override;
void SwapBuffers(const Tegra::FramebufferConfig* framebuffer) override;
- bool TryPresent(int timeout_ms) override;
static std::vector<std::string> EnumerateDevices();
@@ -59,24 +65,26 @@ private:
void Report() const;
- Core::System& system;
+ Core::TelemetrySession& telemetry_session;
+ Core::Memory::Memory& cpu_memory;
Tegra::GPU& gpu;
Common::DynamicLibrary library;
vk::InstanceDispatch dld;
vk::Instance instance;
+ u32 instance_version{};
+
vk::SurfaceKHR surface;
VKScreenInfo screen_info;
vk::DebugCallback debug_callback;
std::unique_ptr<VKDevice> device;
- std::unique_ptr<VKSwapchain> swapchain;
std::unique_ptr<VKMemoryManager> memory_manager;
- std::unique_ptr<VKResourceManager> resource_manager;
std::unique_ptr<StateTracker> state_tracker;
std::unique_ptr<VKScheduler> scheduler;
+ std::unique_ptr<VKSwapchain> swapchain;
std::unique_ptr<VKBlitScreen> blit_screen;
};
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.cpp b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
index a551e3de8..b5b60309e 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.cpp
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.cpp
@@ -12,11 +12,9 @@
#include "common/assert.h"
#include "common/common_types.h"
#include "common/math_util.h"
-
#include "core/core.h"
#include "core/frontend/emu_window.h"
#include "core/memory.h"
-
#include "video_core/gpu.h"
#include "video_core/morton.h"
#include "video_core/rasterizer_interface.h"
@@ -24,8 +22,8 @@
#include "video_core/renderer_vulkan/vk_blit_screen.h"
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_image.h"
+#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_shader_util.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
@@ -210,17 +208,15 @@ struct VKBlitScreen::BufferData {
// Unaligned image data goes here
};
-VKBlitScreen::VKBlitScreen(Core::System& system, Core::Frontend::EmuWindow& render_window,
- VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
- VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
- VKSwapchain& swapchain, VKScheduler& scheduler,
- const VKScreenInfo& screen_info)
- : system{system}, render_window{render_window}, rasterizer{rasterizer}, device{device},
- resource_manager{resource_manager}, memory_manager{memory_manager}, swapchain{swapchain},
- scheduler{scheduler}, image_count{swapchain.GetImageCount()}, screen_info{screen_info} {
- watches.resize(image_count);
- std::generate(watches.begin(), watches.end(),
- []() { return std::make_unique<VKFenceWatch>(); });
+VKBlitScreen::VKBlitScreen(Core::Memory::Memory& cpu_memory_,
+ Core::Frontend::EmuWindow& render_window_,
+ VideoCore::RasterizerInterface& rasterizer_, const VKDevice& device_,
+ VKMemoryManager& memory_manager_, VKSwapchain& swapchain_,
+ VKScheduler& scheduler_, const VKScreenInfo& screen_info_)
+ : cpu_memory{cpu_memory_}, render_window{render_window_}, rasterizer{rasterizer_},
+ device{device_}, memory_manager{memory_manager_}, swapchain{swapchain_},
+ scheduler{scheduler_}, image_count{swapchain.GetImageCount()}, screen_info{screen_info_} {
+ resource_ticks.resize(image_count);
CreateStaticResources();
CreateDynamicResources();
@@ -232,15 +228,16 @@ void VKBlitScreen::Recreate() {
CreateDynamicResources();
}
-std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer,
- bool use_accelerated) {
+VkSemaphore VKBlitScreen::Draw(const Tegra::FramebufferConfig& framebuffer, bool use_accelerated) {
RefreshResources(framebuffer);
// Finish any pending renderpass
scheduler.RequestOutsideRenderPassOperationContext();
const std::size_t image_index = swapchain.GetImageIndex();
- watches[image_index]->Watch(scheduler.GetFence());
+
+ scheduler.Wait(resource_ticks[image_index]);
+ resource_ticks[image_index] = scheduler.CurrentTick();
VKImage* blit_image = use_accelerated ? screen_info.image : raw_images[image_index].get();
@@ -259,7 +256,7 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
const auto pixel_format =
VideoCore::Surface::PixelFormatFromGPUPixelFormat(framebuffer.pixel_format);
const VAddr framebuffer_addr = framebuffer.address + framebuffer.offset;
- const auto host_ptr = system.Memory().GetPointer(framebuffer_addr);
+ const auto host_ptr = cpu_memory.GetPointer(framebuffer_addr);
rasterizer.FlushRegion(ToCacheAddr(host_ptr), GetSizeInBytes(framebuffer));
// TODO(Rodrigo): Read this from HLE
@@ -343,7 +340,7 @@ std::tuple<VKFence&, VkSemaphore> VKBlitScreen::Draw(const Tegra::FramebufferCon
cmdbuf.EndRenderPass();
});
- return {scheduler.GetFence(), *semaphores[image_index]};
+ return *semaphores[image_index];
}
void VKBlitScreen::CreateStaticResources() {
@@ -711,7 +708,7 @@ void VKBlitScreen::CreateFramebuffers() {
void VKBlitScreen::ReleaseRawImages() {
for (std::size_t i = 0; i < raw_images.size(); ++i) {
- watches[i]->Wait();
+ scheduler.Wait(resource_ticks.at(i));
}
raw_images.clear();
raw_buffer_commits.clear();
diff --git a/src/video_core/renderer_vulkan/vk_blit_screen.h b/src/video_core/renderer_vulkan/vk_blit_screen.h
index 243640fab..8f2839214 100644
--- a/src/video_core/renderer_vulkan/vk_blit_screen.h
+++ b/src/video_core/renderer_vulkan/vk_blit_screen.h
@@ -5,16 +5,18 @@
#pragma once
#include <memory>
-#include <tuple>
#include "video_core/renderer_vulkan/vk_memory_manager.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Core {
class System;
}
+namespace Core::Memory {
+class Memory;
+}
+
namespace Core::Frontend {
class EmuWindow;
}
@@ -30,26 +32,26 @@ class RasterizerInterface;
namespace Vulkan {
struct ScreenInfo;
+
class RasterizerVulkan;
class VKDevice;
-class VKFence;
class VKImage;
class VKScheduler;
class VKSwapchain;
class VKBlitScreen final {
public:
- explicit VKBlitScreen(Core::System& system, Core::Frontend::EmuWindow& render_window,
+ explicit VKBlitScreen(Core::Memory::Memory& cpu_memory,
+ Core::Frontend::EmuWindow& render_window,
VideoCore::RasterizerInterface& rasterizer, const VKDevice& device,
- VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
- VKSwapchain& swapchain, VKScheduler& scheduler,
- const VKScreenInfo& screen_info);
+ VKMemoryManager& memory_manager, VKSwapchain& swapchain,
+ VKScheduler& scheduler, const VKScreenInfo& screen_info);
~VKBlitScreen();
void Recreate();
- std::tuple<VKFence&, VkSemaphore> Draw(const Tegra::FramebufferConfig& framebuffer,
- bool use_accelerated);
+ [[nodiscard]] VkSemaphore Draw(const Tegra::FramebufferConfig& framebuffer,
+ bool use_accelerated);
private:
struct BufferData;
@@ -81,11 +83,10 @@ private:
u64 GetRawImageOffset(const Tegra::FramebufferConfig& framebuffer,
std::size_t image_index) const;
- Core::System& system;
+ Core::Memory::Memory& cpu_memory;
Core::Frontend::EmuWindow& render_window;
VideoCore::RasterizerInterface& rasterizer;
const VKDevice& device;
- VKResourceManager& resource_manager;
VKMemoryManager& memory_manager;
VKSwapchain& swapchain;
VKScheduler& scheduler;
@@ -106,7 +107,7 @@ private:
vk::Buffer buffer;
VKMemoryCommit buffer_commit;
- std::vector<std::unique_ptr<VKFenceWatch>> watches;
+ std::vector<u64> resource_ticks;
std::vector<vk::Semaphore> semaphores;
std::vector<std::unique_ptr<VKImage>> raw_images;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
index 1d2f8b557..d9d3da9ea 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.cpp
@@ -145,14 +145,15 @@ void Buffer::CopyFrom(const Buffer& src, std::size_t src_offset, std::size_t dst
});
}
-VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
- const VKDevice& device, VKMemoryManager& memory_manager,
- VKScheduler& scheduler, VKStagingBufferPool& staging_pool)
- : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, system,
- CreateStreamBuffer(device,
- scheduler)},
- device{device}, memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{
- staging_pool} {}
+VKBufferCache::VKBufferCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
+ const VKDevice& device_, VKMemoryManager& memory_manager_,
+ VKScheduler& scheduler_, VKStagingBufferPool& staging_pool_)
+ : VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer>{rasterizer, gpu_memory, cpu_memory,
+ CreateStreamBuffer(device_,
+ scheduler_)},
+ device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{
+ staging_pool_} {}
VKBufferCache::~VKBufferCache() = default;
diff --git a/src/video_core/renderer_vulkan/vk_buffer_cache.h b/src/video_core/renderer_vulkan/vk_buffer_cache.h
index 991ee451c..7fb5ceedf 100644
--- a/src/video_core/renderer_vulkan/vk_buffer_cache.h
+++ b/src/video_core/renderer_vulkan/vk_buffer_cache.h
@@ -13,10 +13,6 @@
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
#include "video_core/renderer_vulkan/wrapper.h"
-namespace Core {
-class System;
-}
-
namespace Vulkan {
class VKDevice;
@@ -53,7 +49,8 @@ private:
class VKBufferCache final : public VideoCommon::BufferCache<Buffer, VkBuffer, VKStreamBuffer> {
public:
- explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer, Core::System& system,
+ explicit VKBufferCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
const VKDevice& device, VKMemoryManager& memory_manager,
VKScheduler& scheduler, VKStagingBufferPool& staging_pool);
~VKBufferCache();
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.cpp b/src/video_core/renderer_vulkan/vk_command_pool.cpp
new file mode 100644
index 000000000..6339f4fe0
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_command_pool.cpp
@@ -0,0 +1,46 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <cstddef>
+
+#include "video_core/renderer_vulkan/vk_command_pool.h"
+#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/wrapper.h"
+
+namespace Vulkan {
+
+constexpr size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
+
+struct CommandPool::Pool {
+ vk::CommandPool handle;
+ vk::CommandBuffers cmdbufs;
+};
+
+CommandPool::CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device)
+ : ResourcePool(master_semaphore, COMMAND_BUFFER_POOL_SIZE), device{device} {}
+
+CommandPool::~CommandPool() = default;
+
+void CommandPool::Allocate(size_t begin, size_t end) {
+ // Command buffers are going to be commited, recorded, executed every single usage cycle.
+ // They are also going to be reseted when commited.
+ Pool& pool = pools.emplace_back();
+ pool.handle = device.GetLogical().CreateCommandPool({
+ .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+ .pNext = nullptr,
+ .flags =
+ VK_COMMAND_POOL_CREATE_TRANSIENT_BIT | VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
+ .queueFamilyIndex = device.GetGraphicsFamily(),
+ });
+ pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
+}
+
+VkCommandBuffer CommandPool::Commit() {
+ const size_t index = CommitResource();
+ const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
+ const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
+ return pools[pool_index].cmdbufs[sub_index];
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_command_pool.h b/src/video_core/renderer_vulkan/vk_command_pool.h
new file mode 100644
index 000000000..b9cb3fb5d
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_command_pool.h
@@ -0,0 +1,34 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <cstddef>
+#include <vector>
+
+#include "video_core/renderer_vulkan/vk_resource_pool.h"
+#include "video_core/renderer_vulkan/wrapper.h"
+
+namespace Vulkan {
+
+class MasterSemaphore;
+class VKDevice;
+
+class CommandPool final : public ResourcePool {
+public:
+ explicit CommandPool(MasterSemaphore& master_semaphore, const VKDevice& device);
+ ~CommandPool() override;
+
+ void Allocate(size_t begin, size_t end) override;
+
+ VkCommandBuffer Commit();
+
+private:
+ struct Pool;
+
+ const VKDevice& device;
+ std::vector<Pool> pools;
+};
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.cpp b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
index 182461ed9..9637c6059 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.cpp
@@ -112,7 +112,8 @@ constexpr u8 quad_array[] = {
0xf9, 0x00, 0x02, 0x00, 0x21, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x23, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4e, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x4c, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x4b, 0x00, 0x00, 0x00,
- 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
+ 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
+};
VkDescriptorSetLayoutBinding BuildQuadArrayPassDescriptorSetLayoutBinding() {
return {
@@ -218,7 +219,8 @@ constexpr u8 uint8_pass[] = {
0x2a, 0x00, 0x00, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x22, 0x00, 0x00, 0x00, 0x23, 0x00, 0x00, 0x00,
0x24, 0x00, 0x00, 0x00, 0x3e, 0x00, 0x03, 0x00, 0x2b, 0x00, 0x00, 0x00, 0x29, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x1d, 0x00, 0x00, 0x00,
- 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
+ 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
+};
// Quad indexed SPIR-V module. Generated from the "shaders/" directory.
constexpr u8 QUAD_INDEXED_SPV[] = {
@@ -341,7 +343,8 @@ constexpr u8 QUAD_INDEXED_SPV[] = {
0xf9, 0x00, 0x02, 0x00, 0x35, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x37, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x76, 0x00, 0x00, 0x00,
0xf9, 0x00, 0x02, 0x00, 0x74, 0x00, 0x00, 0x00, 0xf8, 0x00, 0x02, 0x00, 0x73, 0x00, 0x00, 0x00,
- 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00};
+ 0xfd, 0x00, 0x01, 0x00, 0x38, 0x00, 0x01, 0x00,
+};
std::array<VkDescriptorSetLayoutBinding, 2> BuildInputOutputDescriptorSetBindings() {
return {{
@@ -448,12 +451,12 @@ VKComputePass::VKComputePass(const VKDevice& device, VKDescriptorPool& descripto
VKComputePass::~VKComputePass() = default;
-VkDescriptorSet VKComputePass::CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue,
- VKFence& fence) {
+VkDescriptorSet VKComputePass::CommitDescriptorSet(
+ VKUpdateDescriptorQueue& update_descriptor_queue) {
if (!descriptor_template) {
return nullptr;
}
- const auto set = descriptor_allocator->Commit(fence);
+ const VkDescriptorSet set = descriptor_allocator->Commit();
update_descriptor_queue.Send(*descriptor_template, set);
return set;
}
@@ -477,7 +480,7 @@ std::pair<VkBuffer, VkDeviceSize> QuadArrayPass::Assemble(u32 num_vertices, u32
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
- const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence());
+ const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
@@ -520,13 +523,13 @@ Uint8Pass::~Uint8Pass() = default;
std::pair<VkBuffer, u64> Uint8Pass::Assemble(u32 num_vertices, VkBuffer src_buffer,
u64 src_offset) {
- const auto staging_size = static_cast<u32>(num_vertices * sizeof(u16));
+ const u32 staging_size = static_cast<u32>(num_vertices * sizeof(u16));
auto& buffer = staging_buffer_pool.GetUnusedBuffer(staging_size, false);
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(src_buffer, src_offset, num_vertices);
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
- const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence());
+ const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
@@ -589,7 +592,7 @@ std::pair<VkBuffer, u64> QuadIndexedPass::Assemble(
update_descriptor_queue.Acquire();
update_descriptor_queue.AddBuffer(src_buffer, src_offset, input_size);
update_descriptor_queue.AddBuffer(*buffer.handle, 0, staging_size);
- const auto set = CommitDescriptorSet(update_descriptor_queue, scheduler.GetFence());
+ const VkDescriptorSet set = CommitDescriptorSet(update_descriptor_queue);
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([layout = *layout, pipeline = *pipeline, buffer = *buffer.handle, set,
diff --git a/src/video_core/renderer_vulkan/vk_compute_pass.h b/src/video_core/renderer_vulkan/vk_compute_pass.h
index 230b526bc..acc94f27e 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pass.h
+++ b/src/video_core/renderer_vulkan/vk_compute_pass.h
@@ -15,7 +15,6 @@
namespace Vulkan {
class VKDevice;
-class VKFence;
class VKScheduler;
class VKStagingBufferPool;
class VKUpdateDescriptorQueue;
@@ -30,8 +29,7 @@ public:
~VKComputePass();
protected:
- VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue,
- VKFence& fence);
+ VkDescriptorSet CommitDescriptorSet(VKUpdateDescriptorQueue& update_descriptor_queue);
vk::DescriptorUpdateTemplateKHR descriptor_template;
vk::PipelineLayout layout;
diff --git a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
index ed9d2991c..9be72dc9b 100644
--- a/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_compute_pipeline.cpp
@@ -32,7 +32,7 @@ VkDescriptorSet VKComputePipeline::CommitDescriptorSet() {
if (!descriptor_template) {
return {};
}
- const auto set = descriptor_allocator.Commit(scheduler.GetFence());
+ const VkDescriptorSet set = descriptor_allocator.Commit();
update_descriptor_queue.Send(*descriptor_template, set);
return set;
}
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
index ac4a0884e..f38e089d5 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.cpp
@@ -7,7 +7,8 @@
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_descriptor_pool.h"
#include "video_core/renderer_vulkan/vk_device.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
+#include "video_core/renderer_vulkan/vk_resource_pool.h"
+#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
@@ -15,14 +16,15 @@ namespace Vulkan {
// Prefer small grow rates to avoid saturating the descriptor pool with barely used pipelines.
constexpr std::size_t SETS_GROW_RATE = 0x20;
-DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool,
- VkDescriptorSetLayout layout)
- : VKFencedPool{SETS_GROW_RATE}, descriptor_pool{descriptor_pool}, layout{layout} {}
+DescriptorAllocator::DescriptorAllocator(VKDescriptorPool& descriptor_pool_,
+ VkDescriptorSetLayout layout_)
+ : ResourcePool(descriptor_pool_.master_semaphore, SETS_GROW_RATE),
+ descriptor_pool{descriptor_pool_}, layout{layout_} {}
DescriptorAllocator::~DescriptorAllocator() = default;
-VkDescriptorSet DescriptorAllocator::Commit(VKFence& fence) {
- const std::size_t index = CommitResource(fence);
+VkDescriptorSet DescriptorAllocator::Commit() {
+ const std::size_t index = CommitResource();
return descriptors_allocations[index / SETS_GROW_RATE][index % SETS_GROW_RATE];
}
@@ -30,8 +32,9 @@ void DescriptorAllocator::Allocate(std::size_t begin, std::size_t end) {
descriptors_allocations.push_back(descriptor_pool.AllocateDescriptors(layout, end - begin));
}
-VKDescriptorPool::VKDescriptorPool(const VKDevice& device)
- : device{device}, active_pool{AllocateNewPool()} {}
+VKDescriptorPool::VKDescriptorPool(const VKDevice& device_, VKScheduler& scheduler)
+ : device{device_}, master_semaphore{scheduler.GetMasterSemaphore()}, active_pool{
+ AllocateNewPool()} {}
VKDescriptorPool::~VKDescriptorPool() = default;
diff --git a/src/video_core/renderer_vulkan/vk_descriptor_pool.h b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
index 9efa66bef..544f32a20 100644
--- a/src/video_core/renderer_vulkan/vk_descriptor_pool.h
+++ b/src/video_core/renderer_vulkan/vk_descriptor_pool.h
@@ -6,21 +6,24 @@
#include <vector>
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
+#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
+class VKDevice;
class VKDescriptorPool;
+class VKScheduler;
-class DescriptorAllocator final : public VKFencedPool {
+class DescriptorAllocator final : public ResourcePool {
public:
explicit DescriptorAllocator(VKDescriptorPool& descriptor_pool, VkDescriptorSetLayout layout);
~DescriptorAllocator() override;
+ DescriptorAllocator& operator=(const DescriptorAllocator&) = delete;
DescriptorAllocator(const DescriptorAllocator&) = delete;
- VkDescriptorSet Commit(VKFence& fence);
+ VkDescriptorSet Commit();
protected:
void Allocate(std::size_t begin, std::size_t end) override;
@@ -36,15 +39,19 @@ class VKDescriptorPool final {
friend DescriptorAllocator;
public:
- explicit VKDescriptorPool(const VKDevice& device);
+ explicit VKDescriptorPool(const VKDevice& device, VKScheduler& scheduler);
~VKDescriptorPool();
+ VKDescriptorPool(const VKDescriptorPool&) = delete;
+ VKDescriptorPool& operator=(const VKDescriptorPool&) = delete;
+
private:
vk::DescriptorPool* AllocateNewPool();
vk::DescriptorSets AllocateDescriptors(VkDescriptorSetLayout layout, std::size_t count);
const VKDevice& device;
+ MasterSemaphore& master_semaphore;
std::vector<vk::DescriptorPool> pools;
vk::DescriptorPool* active_pool;
diff --git a/src/video_core/renderer_vulkan/vk_device.cpp b/src/video_core/renderer_vulkan/vk_device.cpp
index ebcfaa0e3..f34ed6735 100644
--- a/src/video_core/renderer_vulkan/vk_device.cpp
+++ b/src/video_core/renderer_vulkan/vk_device.cpp
@@ -38,10 +38,14 @@ constexpr std::array Depth16UnormS8_UINT{
constexpr std::array REQUIRED_EXTENSIONS{
VK_KHR_SWAPCHAIN_EXTENSION_NAME,
+ VK_KHR_MAINTENANCE1_EXTENSION_NAME,
+ VK_KHR_STORAGE_BUFFER_STORAGE_CLASS_EXTENSION_NAME,
+ VK_KHR_SHADER_DRAW_PARAMETERS_EXTENSION_NAME,
VK_KHR_16BIT_STORAGE_EXTENSION_NAME,
VK_KHR_8BIT_STORAGE_EXTENSION_NAME,
VK_KHR_DRIVER_PROPERTIES_EXTENSION_NAME,
VK_KHR_DESCRIPTOR_UPDATE_TEMPLATE_EXTENSION_NAME,
+ VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,
VK_EXT_VERTEX_ATTRIBUTE_DIVISOR_EXTENSION_NAME,
VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,
VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,
@@ -78,6 +82,21 @@ VkFormatFeatureFlags GetFormatFeatures(VkFormatProperties properties, FormatType
}
}
+[[nodiscard]] bool IsRDNA(std::string_view device_name, VkDriverIdKHR driver_id) {
+ static constexpr std::array RDNA_DEVICES{
+ "5700",
+ "5600",
+ "5500",
+ "5300",
+ };
+ if (driver_id != VK_DRIVER_ID_AMD_PROPRIETARY_KHR) {
+ return false;
+ }
+ return std::any_of(RDNA_DEVICES.begin(), RDNA_DEVICES.end(), [device_name](const char* name) {
+ return device_name.find(name) != std::string_view::npos;
+ });
+}
+
std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
vk::PhysicalDevice physical, const vk::InstanceDispatch& dld) {
static constexpr std::array formats{
@@ -171,10 +190,10 @@ std::unordered_map<VkFormat, VkFormatProperties> GetFormatProperties(
} // Anonymous namespace
-VKDevice::VKDevice(VkInstance instance, vk::PhysicalDevice physical, VkSurfaceKHR surface,
- const vk::InstanceDispatch& dld)
- : dld{dld}, physical{physical}, properties{physical.GetProperties()},
- format_properties{GetFormatProperties(physical, dld)} {
+VKDevice::VKDevice(VkInstance instance_, u32 instance_version_, vk::PhysicalDevice physical_,
+ VkSurfaceKHR surface, const vk::InstanceDispatch& dld_)
+ : dld{dld_}, physical{physical_}, properties{physical.GetProperties()},
+ instance_version{instance_version_}, format_properties{GetFormatProperties(physical, dld)} {
SetupFamilies(surface);
SetupFeatures();
}
@@ -250,6 +269,13 @@ bool VKDevice::Create() {
.inheritedQueries = false,
};
+ VkPhysicalDeviceTimelineSemaphoreFeaturesKHR timeline_semaphore{
+ .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES_KHR,
+ .pNext = nullptr,
+ .timelineSemaphore = true,
+ };
+ SetNext(next, timeline_semaphore);
+
VkPhysicalDevice16BitStorageFeaturesKHR bit16_storage{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_16BIT_STORAGE_FEATURES_KHR,
.pNext = nullptr,
@@ -380,6 +406,15 @@ bool VKDevice::Create() {
CollectTelemetryParameters();
+ if (ext_extended_dynamic_state && IsRDNA(properties.deviceName, driver_id)) {
+ // AMD's proprietary driver supports VK_EXT_extended_dynamic_state but on RDNA devices it
+ // seems to cause stability issues
+ LOG_WARNING(
+ Render_Vulkan,
+ "Blacklisting AMD proprietary on RDNA devices from VK_EXT_extended_dynamic_state");
+ ext_extended_dynamic_state = false;
+ }
+
graphics_queue = logical.GetQueue(graphics_family);
present_queue = logical.GetQueue(present_family);
@@ -565,20 +600,6 @@ bool VKDevice::IsSuitable(vk::PhysicalDevice physical, VkSurfaceKHR surface) {
std::vector<const char*> VKDevice::LoadExtensions() {
std::vector<const char*> extensions;
- const auto Test = [&](const VkExtensionProperties& extension,
- std::optional<std::reference_wrapper<bool>> status, const char* name,
- bool push) {
- if (extension.extensionName != std::string_view(name)) {
- return;
- }
- if (push) {
- extensions.push_back(name);
- }
- if (status) {
- status->get() = true;
- }
- };
-
extensions.reserve(7 + REQUIRED_EXTENSIONS.size());
extensions.insert(extensions.begin(), REQUIRED_EXTENSIONS.begin(), REQUIRED_EXTENSIONS.end());
@@ -587,28 +608,36 @@ std::vector<const char*> VKDevice::LoadExtensions() {
bool has_ext_transform_feedback{};
bool has_ext_custom_border_color{};
bool has_ext_extended_dynamic_state{};
- for (const auto& extension : physical.EnumerateDeviceExtensionProperties()) {
- Test(extension, nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
- Test(extension, khr_uniform_buffer_standard_layout,
+ for (const VkExtensionProperties& extension : physical.EnumerateDeviceExtensionProperties()) {
+ const auto test = [&](std::optional<std::reference_wrapper<bool>> status, const char* name,
+ bool push) {
+ if (extension.extensionName != std::string_view(name)) {
+ return;
+ }
+ if (push) {
+ extensions.push_back(name);
+ }
+ if (status) {
+ status->get() = true;
+ }
+ };
+ test(nv_viewport_swizzle, VK_NV_VIEWPORT_SWIZZLE_EXTENSION_NAME, true);
+ test(khr_uniform_buffer_standard_layout,
VK_KHR_UNIFORM_BUFFER_STANDARD_LAYOUT_EXTENSION_NAME, true);
- Test(extension, has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,
- false);
- Test(extension, ext_depth_range_unrestricted,
- VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
- Test(extension, ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
- Test(extension, ext_shader_viewport_index_layer,
- VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME, true);
- Test(extension, has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,
- false);
- Test(extension, has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME,
- false);
- Test(extension, has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME,
- false);
- Test(extension, has_ext_extended_dynamic_state,
- VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
+ test(has_khr_shader_float16_int8, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, false);
+ test(ext_depth_range_unrestricted, VK_EXT_DEPTH_RANGE_UNRESTRICTED_EXTENSION_NAME, true);
+ test(ext_index_type_uint8, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, true);
+ test(ext_shader_viewport_index_layer, VK_EXT_SHADER_VIEWPORT_INDEX_LAYER_EXTENSION_NAME,
+ true);
+ test(has_ext_transform_feedback, VK_EXT_TRANSFORM_FEEDBACK_EXTENSION_NAME, false);
+ test(has_ext_custom_border_color, VK_EXT_CUSTOM_BORDER_COLOR_EXTENSION_NAME, false);
+ test(has_ext_extended_dynamic_state, VK_EXT_EXTENDED_DYNAMIC_STATE_EXTENSION_NAME, false);
+ if (instance_version >= VK_API_VERSION_1_1) {
+ test(has_ext_subgroup_size_control, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false);
+ }
if (Settings::values.renderer_debug) {
- Test(extension, nv_device_diagnostics_config,
- VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME, true);
+ test(nv_device_diagnostics_config, VK_NV_DEVICE_DIAGNOSTICS_CONFIG_EXTENSION_NAME,
+ true);
}
}
@@ -742,13 +771,18 @@ void VKDevice::CollectTelemetryParameters() {
VkPhysicalDeviceDriverPropertiesKHR driver{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_DRIVER_PROPERTIES_KHR,
.pNext = nullptr,
+ .driverID = {},
+ .driverName = {},
+ .driverInfo = {},
+ .conformanceVersion = {},
};
- VkPhysicalDeviceProperties2KHR properties{
+ VkPhysicalDeviceProperties2KHR device_properties{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2_KHR,
.pNext = &driver,
+ .properties = {},
};
- physical.GetProperties2KHR(properties);
+ physical.GetProperties2KHR(device_properties);
driver_id = driver.driverID;
vendor_name = driver.driverName;
diff --git a/src/video_core/renderer_vulkan/vk_device.h b/src/video_core/renderer_vulkan/vk_device.h
index 26a233db1..4286673d9 100644
--- a/src/video_core/renderer_vulkan/vk_device.h
+++ b/src/video_core/renderer_vulkan/vk_device.h
@@ -24,8 +24,8 @@ const u32 GuestWarpSize = 32;
/// Handles data specific to a physical device.
class VKDevice final {
public:
- explicit VKDevice(VkInstance instance, vk::PhysicalDevice physical, VkSurfaceKHR surface,
- const vk::InstanceDispatch& dld);
+ explicit VKDevice(VkInstance instance, u32 instance_version, vk::PhysicalDevice physical,
+ VkSurfaceKHR surface, const vk::InstanceDispatch& dld);
~VKDevice();
/// Initializes the device. Returns true on success.
@@ -82,8 +82,13 @@ public:
return present_family;
}
+ /// Returns the current instance Vulkan API version in Vulkan-formatted version numbers.
+ u32 InstanceApiVersion() const {
+ return instance_version;
+ }
+
/// Returns the current Vulkan API version provided in Vulkan-formatted version numbers.
- u32 GetApiVersion() const {
+ u32 ApiVersion() const {
return properties.apiVersion;
}
@@ -239,6 +244,7 @@ private:
vk::Device logical; ///< Logical device.
vk::Queue graphics_queue; ///< Main graphics queue.
vk::Queue present_queue; ///< Main present queue.
+ u32 instance_version{}; ///< Vulkan onstance version.
u32 graphics_family{}; ///< Main graphics queue family index.
u32 present_family{}; ///< Main present queue family index.
VkDriverIdKHR driver_id{}; ///< Driver ID.
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.cpp b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
index d7f65d435..5babbdd0b 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.cpp
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.cpp
@@ -29,8 +29,8 @@ void InnerFence::Queue() {
}
ASSERT(!event);
- event = device.GetLogical().CreateNewEvent();
- ticks = scheduler.Ticks();
+ event = device.GetLogical().CreateEvent();
+ ticks = scheduler.CurrentTick();
scheduler.RequestOutsideRenderPassOperationContext();
scheduler.Record([event = *event](vk::CommandBuffer cmdbuf) {
@@ -52,7 +52,7 @@ void InnerFence::Wait() {
}
ASSERT(event);
- if (ticks >= scheduler.Ticks()) {
+ if (ticks >= scheduler.CurrentTick()) {
scheduler.Flush();
}
while (!IsEventSignalled()) {
@@ -71,12 +71,12 @@ bool InnerFence::IsEventSignalled() const {
}
}
-VKFenceManager::VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- const VKDevice& device, VKScheduler& scheduler,
- VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
- VKQueryCache& query_cache)
- : GenericFenceManager(system, rasterizer, texture_cache, buffer_cache, query_cache),
- device{device}, scheduler{scheduler} {}
+VKFenceManager::VKFenceManager(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
+ Tegra::MemoryManager& memory_manager, VKTextureCache& texture_cache,
+ VKBufferCache& buffer_cache, VKQueryCache& query_cache,
+ const VKDevice& device_, VKScheduler& scheduler_)
+ : GenericFenceManager(rasterizer, gpu, texture_cache, buffer_cache, query_cache),
+ device{device_}, scheduler{scheduler_} {}
Fence VKFenceManager::CreateFence(u32 value, bool is_stubbed) {
return std::make_shared<InnerFence>(device, scheduler, value, is_stubbed);
diff --git a/src/video_core/renderer_vulkan/vk_fence_manager.h b/src/video_core/renderer_vulkan/vk_fence_manager.h
index 043fe7947..1547d6d30 100644
--- a/src/video_core/renderer_vulkan/vk_fence_manager.h
+++ b/src/video_core/renderer_vulkan/vk_fence_manager.h
@@ -55,10 +55,10 @@ using GenericFenceManager =
class VKFenceManager final : public GenericFenceManager {
public:
- explicit VKFenceManager(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- const VKDevice& device, VKScheduler& scheduler,
- VKTextureCache& texture_cache, VKBufferCache& buffer_cache,
- VKQueryCache& query_cache);
+ explicit VKFenceManager(VideoCore::RasterizerInterface& rasterizer, Tegra::GPU& gpu,
+ Tegra::MemoryManager& memory_manager, VKTextureCache& texture_cache,
+ VKBufferCache& buffer_cache, VKQueryCache& query_cache,
+ const VKDevice& device, VKScheduler& scheduler);
protected:
Fence CreateFence(u32 value, bool is_stubbed) override;
diff --git a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
index 2e46c6278..0e8f9c352 100644
--- a/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
+++ b/src/video_core/renderer_vulkan/vk_graphics_pipeline.cpp
@@ -93,7 +93,7 @@ VkDescriptorSet VKGraphicsPipeline::CommitDescriptorSet() {
if (!descriptor_template) {
return {};
}
- const auto set = descriptor_allocator.Commit(scheduler.GetFence());
+ const VkDescriptorSet set = descriptor_allocator.Commit();
update_descriptor_queue.Send(*descriptor_template, set);
return set;
}
@@ -159,6 +159,7 @@ std::vector<vk::ShaderModule> VKGraphicsPipeline::CreateShaderModules(
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
+ .codeSize = 0,
};
std::vector<vk::ShaderModule> modules;
@@ -261,12 +262,12 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
vertex_input_ci.pNext = &input_divisor_ci;
}
- const auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology());
+ const auto input_assembly_topology = MaxwellToVK::PrimitiveTopology(device, state.topology);
const VkPipelineInputAssemblyStateCreateInfo input_assembly_ci{
.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
- .topology = MaxwellToVK::PrimitiveTopology(device, dynamic.Topology()),
+ .topology = MaxwellToVK::PrimitiveTopology(device, state.topology),
.primitiveRestartEnable = state.primitive_restart_enable != 0 &&
SupportsPrimitiveRestart(input_assembly_topology),
};
@@ -388,6 +389,7 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
.logicOp = VK_LOGIC_OP_COPY,
.attachmentCount = static_cast<u32>(num_attachments),
.pAttachments = cb_attachments.data(),
+ .blendConstants = {},
};
std::vector dynamic_states{
@@ -400,7 +402,6 @@ vk::Pipeline VKGraphicsPipeline::CreatePipeline(const RenderPassParams& renderpa
static constexpr std::array extended{
VK_DYNAMIC_STATE_CULL_MODE_EXT,
VK_DYNAMIC_STATE_FRONT_FACE_EXT,
- VK_DYNAMIC_STATE_PRIMITIVE_TOPOLOGY_EXT,
VK_DYNAMIC_STATE_VERTEX_INPUT_BINDING_STRIDE_EXT,
VK_DYNAMIC_STATE_DEPTH_TEST_ENABLE_EXT,
VK_DYNAMIC_STATE_DEPTH_WRITE_ENABLE_EXT,
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.cpp b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
new file mode 100644
index 000000000..ae26e558d
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.cpp
@@ -0,0 +1,56 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <atomic>
+#include <chrono>
+
+#include "core/settings.h"
+#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/vk_master_semaphore.h"
+#include "video_core/renderer_vulkan/wrapper.h"
+
+namespace Vulkan {
+
+using namespace std::chrono_literals;
+
+MasterSemaphore::MasterSemaphore(const VKDevice& device) {
+ static constexpr VkSemaphoreTypeCreateInfoKHR semaphore_type_ci{
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO_KHR,
+ .pNext = nullptr,
+ .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE_KHR,
+ .initialValue = 0,
+ };
+ static constexpr VkSemaphoreCreateInfo semaphore_ci{
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
+ .pNext = &semaphore_type_ci,
+ .flags = 0,
+ };
+ semaphore = device.GetLogical().CreateSemaphore(semaphore_ci);
+
+ if (!Settings::values.renderer_debug) {
+ return;
+ }
+ // Validation layers have a bug where they fail to track resource usage when using timeline
+ // semaphores and synchronizing with GetSemaphoreCounterValueKHR. To workaround this issue, have
+ // a separate thread waiting for each timeline semaphore value.
+ debug_thread = std::thread([this] {
+ u64 counter = 0;
+ while (!shutdown) {
+ if (semaphore.Wait(counter, 10'000'000)) {
+ ++counter;
+ }
+ }
+ });
+}
+
+MasterSemaphore::~MasterSemaphore() {
+ shutdown = true;
+
+ // This thread might not be started
+ if (debug_thread.joinable()) {
+ debug_thread.join();
+ }
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_master_semaphore.h b/src/video_core/renderer_vulkan/vk_master_semaphore.h
new file mode 100644
index 000000000..0e93706d7
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_master_semaphore.h
@@ -0,0 +1,70 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <atomic>
+#include <thread>
+
+#include "common/common_types.h"
+#include "video_core/renderer_vulkan/wrapper.h"
+
+namespace Vulkan {
+
+class VKDevice;
+
+class MasterSemaphore {
+public:
+ explicit MasterSemaphore(const VKDevice& device);
+ ~MasterSemaphore();
+
+ /// Returns the current logical tick.
+ [[nodiscard]] u64 CurrentTick() const noexcept {
+ return current_tick;
+ }
+
+ /// Returns the timeline semaphore handle.
+ [[nodiscard]] VkSemaphore Handle() const noexcept {
+ return *semaphore;
+ }
+
+ /// Returns true when a tick has been hit by the GPU.
+ [[nodiscard]] bool IsFree(u64 tick) {
+ return gpu_tick >= tick;
+ }
+
+ /// Advance to the logical tick.
+ void NextTick() noexcept {
+ ++current_tick;
+ }
+
+ /// Refresh the known GPU tick
+ void Refresh() {
+ gpu_tick = semaphore.GetCounter();
+ }
+
+ /// Waits for a tick to be hit on the GPU
+ void Wait(u64 tick) {
+ // No need to wait if the GPU is ahead of the tick
+ if (IsFree(tick)) {
+ return;
+ }
+ // Update the GPU tick and try again
+ Refresh();
+ if (IsFree(tick)) {
+ return;
+ }
+ // If none of the above is hit, fallback to a regular wait
+ semaphore.Wait(tick);
+ }
+
+private:
+ vk::Semaphore semaphore; ///< Timeline semaphore.
+ std::atomic<u64> gpu_tick{0}; ///< Current known GPU tick.
+ std::atomic<u64> current_tick{1}; ///< Current logical tick.
+ std::atomic<bool> shutdown{false}; ///< True when the object is being destroyed.
+ std::thread debug_thread; ///< Debug thread to workaround validation layer bugs.
+};
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
index cfdcdd6ab..dedc9c466 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.cpp
@@ -135,64 +135,56 @@ bool ComputePipelineCacheKey::operator==(const ComputePipelineCacheKey& rhs) con
return std::memcmp(&rhs, this, sizeof *this) == 0;
}
-Shader::Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr,
- VideoCommon::Shader::ProgramCode program_code, u32 main_offset)
- : gpu_addr{gpu_addr}, program_code{std::move(program_code)},
- registry{stage, GetEngine(system, stage)}, shader_ir{this->program_code, main_offset,
- compiler_settings, registry},
- entries{GenerateShaderEntries(shader_ir)} {}
+Shader::Shader(Tegra::Engines::ConstBufferEngineInterface& engine, Tegra::Engines::ShaderType stage,
+ GPUVAddr gpu_addr_, VAddr cpu_addr, VideoCommon::Shader::ProgramCode program_code_,
+ u32 main_offset)
+ : gpu_addr(gpu_addr_), program_code(std::move(program_code_)), registry(stage, engine),
+ shader_ir(program_code, main_offset, compiler_settings, registry),
+ entries(GenerateShaderEntries(shader_ir)) {}
Shader::~Shader() = default;
-Tegra::Engines::ConstBufferEngineInterface& Shader::GetEngine(Core::System& system,
- Tegra::Engines::ShaderType stage) {
- if (stage == ShaderType::Compute) {
- return system.GPU().KeplerCompute();
- } else {
- return system.GPU().Maxwell3D();
- }
-}
-
-VKPipelineCache::VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer,
- const VKDevice& device, VKScheduler& scheduler,
- VKDescriptorPool& descriptor_pool,
- VKUpdateDescriptorQueue& update_descriptor_queue,
- VKRenderPassCache& renderpass_cache)
- : VideoCommon::ShaderCache<Shader>{rasterizer}, system{system}, device{device},
- scheduler{scheduler}, descriptor_pool{descriptor_pool},
- update_descriptor_queue{update_descriptor_queue}, renderpass_cache{renderpass_cache} {}
+VKPipelineCache::VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu_,
+ Tegra::Engines::Maxwell3D& maxwell3d_,
+ Tegra::Engines::KeplerCompute& kepler_compute_,
+ Tegra::MemoryManager& gpu_memory_, const VKDevice& device_,
+ VKScheduler& scheduler_, VKDescriptorPool& descriptor_pool_,
+ VKUpdateDescriptorQueue& update_descriptor_queue_,
+ VKRenderPassCache& renderpass_cache_)
+ : VideoCommon::ShaderCache<Shader>{rasterizer}, gpu{gpu_}, maxwell3d{maxwell3d_},
+ kepler_compute{kepler_compute_}, gpu_memory{gpu_memory_}, device{device_},
+ scheduler{scheduler_}, descriptor_pool{descriptor_pool_},
+ update_descriptor_queue{update_descriptor_queue_}, renderpass_cache{renderpass_cache_} {}
VKPipelineCache::~VKPipelineCache() = default;
std::array<Shader*, Maxwell::MaxShaderProgram> VKPipelineCache::GetShaders() {
- const auto& gpu = system.GPU().Maxwell3D();
-
std::array<Shader*, Maxwell::MaxShaderProgram> shaders{};
+
for (std::size_t index = 0; index < Maxwell::MaxShaderProgram; ++index) {
const auto program{static_cast<Maxwell::ShaderProgram>(index)};
// Skip stages that are not enabled
- if (!gpu.regs.IsShaderConfigEnabled(index)) {
+ if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
continue;
}
- auto& memory_manager{system.GPU().MemoryManager()};
- const GPUVAddr program_addr{GetShaderAddress(system, program)};
- const std::optional cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
+ const GPUVAddr gpu_addr{GetShaderAddress(maxwell3d, program)};
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
ASSERT(cpu_addr);
Shader* result = cpu_addr ? TryGet(*cpu_addr) : null_shader.get();
if (!result) {
- const auto host_ptr{memory_manager.GetPointer(program_addr)};
+ const u8* const host_ptr{gpu_memory.GetPointer(gpu_addr)};
// No shader found - create a new one
- constexpr u32 stage_offset = STAGE_MAIN_OFFSET;
+ static constexpr u32 stage_offset = STAGE_MAIN_OFFSET;
const auto stage = static_cast<ShaderType>(index == 0 ? 0 : index - 1);
- ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, false);
+ ProgramCode code = GetShaderCode(gpu_memory, gpu_addr, host_ptr, false);
const std::size_t size_in_bytes = code.size() * sizeof(u64);
- auto shader = std::make_unique<Shader>(system, stage, program_addr, std::move(code),
- stage_offset);
+ auto shader = std::make_unique<Shader>(maxwell3d, stage, gpu_addr, *cpu_addr,
+ std::move(code), stage_offset);
result = shader.get();
if (cpu_addr) {
@@ -215,11 +207,11 @@ VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
}
last_graphics_key = key;
- if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(system.GPU())) {
+ if (device.UseAsynchronousShaders() && async_shaders.IsShaderAsync(gpu)) {
std::unique_lock lock{pipeline_cache};
const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
if (is_cache_miss) {
- system.GPU().ShaderNotify().MarkSharderBuilding();
+ gpu.ShaderNotify().MarkSharderBuilding();
LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
const auto [program, bindings] = DecompileShaders(key.fixed_state);
async_shaders.QueueVulkanShader(this, device, scheduler, descriptor_pool,
@@ -233,13 +225,13 @@ VKGraphicsPipeline* VKPipelineCache::GetGraphicsPipeline(
const auto [pair, is_cache_miss] = graphics_cache.try_emplace(key);
auto& entry = pair->second;
if (is_cache_miss) {
- system.GPU().ShaderNotify().MarkSharderBuilding();
+ gpu.ShaderNotify().MarkSharderBuilding();
LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
const auto [program, bindings] = DecompileShaders(key.fixed_state);
entry = std::make_unique<VKGraphicsPipeline>(device, scheduler, descriptor_pool,
update_descriptor_queue, renderpass_cache, key,
bindings, program);
- system.GPU().ShaderNotify().MarkShaderComplete();
+ gpu.ShaderNotify().MarkShaderComplete();
}
last_graphics_pipeline = entry.get();
return last_graphics_pipeline;
@@ -255,22 +247,21 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
}
LOG_INFO(Render_Vulkan, "Compile 0x{:016X}", key.Hash());
- auto& memory_manager = system.GPU().MemoryManager();
- const auto program_addr = key.shader;
+ const GPUVAddr gpu_addr = key.shader;
- const auto cpu_addr = memory_manager.GpuToCpuAddress(program_addr);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
ASSERT(cpu_addr);
Shader* shader = cpu_addr ? TryGet(*cpu_addr) : null_kernel.get();
if (!shader) {
// No shader found - create a new one
- const auto host_ptr = memory_manager.GetPointer(program_addr);
+ const auto host_ptr = gpu_memory.GetPointer(gpu_addr);
- ProgramCode code = GetShaderCode(memory_manager, program_addr, host_ptr, true);
+ ProgramCode code = GetShaderCode(gpu_memory, gpu_addr, host_ptr, true);
const std::size_t size_in_bytes = code.size() * sizeof(u64);
- auto shader_info = std::make_unique<Shader>(system, ShaderType::Compute, program_addr,
- std::move(code), KERNEL_MAIN_OFFSET);
+ auto shader_info = std::make_unique<Shader>(kepler_compute, ShaderType::Compute, gpu_addr,
+ *cpu_addr, std::move(code), KERNEL_MAIN_OFFSET);
shader = shader_info.get();
if (cpu_addr) {
@@ -298,7 +289,7 @@ VKComputePipeline& VKPipelineCache::GetComputePipeline(const ComputePipelineCach
}
void VKPipelineCache::EmplacePipeline(std::unique_ptr<VKGraphicsPipeline> pipeline) {
- system.GPU().ShaderNotify().MarkShaderComplete();
+ gpu.ShaderNotify().MarkShaderComplete();
std::unique_lock lock{pipeline_cache};
graphics_cache.at(pipeline->GetCacheKey()) = std::move(pipeline);
}
@@ -339,12 +330,8 @@ void VKPipelineCache::OnShaderRemoval(Shader* shader) {
std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>>
VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
- auto& memory_manager = system.GPU().MemoryManager();
- const auto& gpu = system.GPU().Maxwell3D();
-
Specialization specialization;
- if (fixed_state.dynamic_state.Topology() == Maxwell::PrimitiveTopology::Points ||
- device.IsExtExtendedDynamicStateSupported()) {
+ if (fixed_state.topology == Maxwell::PrimitiveTopology::Points) {
float point_size;
std::memcpy(&point_size, &fixed_state.point_size, sizeof(float));
specialization.point_size = point_size;
@@ -364,12 +351,12 @@ VKPipelineCache::DecompileShaders(const FixedPipelineState& fixed_state) {
const auto program_enum = static_cast<Maxwell::ShaderProgram>(index);
// Skip stages that are not enabled
- if (!gpu.regs.IsShaderConfigEnabled(index)) {
+ if (!maxwell3d.regs.IsShaderConfigEnabled(index)) {
continue;
}
- const GPUVAddr gpu_addr = GetShaderAddress(system, program_enum);
- const std::optional<VAddr> cpu_addr = memory_manager.GpuToCpuAddress(gpu_addr);
+ const GPUVAddr gpu_addr = GetShaderAddress(maxwell3d, program_enum);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
Shader* const shader = cpu_addr ? TryGet(*cpu_addr) : null_shader.get();
const std::size_t stage = index == 0 ? 0 : index - 1; // Stage indices are 0 - 5
diff --git a/src/video_core/renderer_vulkan/vk_pipeline_cache.h b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
index c04829e77..e558e6658 100644
--- a/src/video_core/renderer_vulkan/vk_pipeline_cache.h
+++ b/src/video_core/renderer_vulkan/vk_pipeline_cache.h
@@ -38,7 +38,6 @@ class RasterizerVulkan;
class VKComputePipeline;
class VKDescriptorPool;
class VKDevice;
-class VKFence;
class VKScheduler;
class VKUpdateDescriptorQueue;
@@ -85,7 +84,8 @@ namespace Vulkan {
class Shader {
public:
- explicit Shader(Core::System& system, Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr,
+ explicit Shader(Tegra::Engines::ConstBufferEngineInterface& engine,
+ Tegra::Engines::ShaderType stage, GPUVAddr gpu_addr, VAddr cpu_addr,
VideoCommon::Shader::ProgramCode program_code, u32 main_offset);
~Shader();
@@ -97,22 +97,19 @@ public:
return shader_ir;
}
- const VideoCommon::Shader::Registry& GetRegistry() const {
- return registry;
- }
-
const VideoCommon::Shader::ShaderIR& GetIR() const {
return shader_ir;
}
+ const VideoCommon::Shader::Registry& GetRegistry() const {
+ return registry;
+ }
+
const ShaderEntries& GetEntries() const {
return entries;
}
private:
- static Tegra::Engines::ConstBufferEngineInterface& GetEngine(Core::System& system,
- Tegra::Engines::ShaderType stage);
-
GPUVAddr gpu_addr{};
VideoCommon::Shader::ProgramCode program_code;
VideoCommon::Shader::Registry registry;
@@ -122,9 +119,11 @@ private:
class VKPipelineCache final : public VideoCommon::ShaderCache<Shader> {
public:
- explicit VKPipelineCache(Core::System& system, RasterizerVulkan& rasterizer,
- const VKDevice& device, VKScheduler& scheduler,
- VKDescriptorPool& descriptor_pool,
+ explicit VKPipelineCache(RasterizerVulkan& rasterizer, Tegra::GPU& gpu,
+ Tegra::Engines::Maxwell3D& maxwell3d,
+ Tegra::Engines::KeplerCompute& kepler_compute,
+ Tegra::MemoryManager& gpu_memory, const VKDevice& device,
+ VKScheduler& scheduler, VKDescriptorPool& descriptor_pool,
VKUpdateDescriptorQueue& update_descriptor_queue,
VKRenderPassCache& renderpass_cache);
~VKPipelineCache() override;
@@ -145,7 +144,11 @@ private:
std::pair<SPIRVProgram, std::vector<VkDescriptorSetLayoutBinding>> DecompileShaders(
const FixedPipelineState& fixed_state);
- Core::System& system;
+ Tegra::GPU& gpu;
+ Tegra::Engines::Maxwell3D& maxwell3d;
+ Tegra::Engines::KeplerCompute& kepler_compute;
+ Tegra::MemoryManager& gpu_memory;
+
const VKDevice& device;
VKScheduler& scheduler;
VKDescriptorPool& descriptor_pool;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.cpp b/src/video_core/renderer_vulkan/vk_query_cache.cpp
index 6cd63d090..ee2d871e3 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_query_cache.cpp
@@ -9,35 +9,33 @@
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
+#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
+using VideoCore::QueryType;
+
namespace {
constexpr std::array QUERY_TARGETS = {VK_QUERY_TYPE_OCCLUSION};
-constexpr VkQueryType GetTarget(VideoCore::QueryType type) {
+constexpr VkQueryType GetTarget(QueryType type) {
return QUERY_TARGETS[static_cast<std::size_t>(type)];
}
} // Anonymous namespace
-QueryPool::QueryPool() : VKFencedPool{GROW_STEP} {}
+QueryPool::QueryPool(const VKDevice& device_, VKScheduler& scheduler, QueryType type_)
+ : ResourcePool{scheduler.GetMasterSemaphore(), GROW_STEP}, device{device_}, type{type_} {}
QueryPool::~QueryPool() = default;
-void QueryPool::Initialize(const VKDevice& device_, VideoCore::QueryType type_) {
- device = &device_;
- type = type_;
-}
-
-std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) {
+std::pair<VkQueryPool, u32> QueryPool::Commit() {
std::size_t index;
do {
- index = CommitResource(fence);
+ index = CommitResource();
} while (usage[index]);
usage[index] = true;
@@ -47,7 +45,7 @@ std::pair<VkQueryPool, u32> QueryPool::Commit(VKFence& fence) {
void QueryPool::Allocate(std::size_t begin, std::size_t end) {
usage.resize(end);
- pools.push_back(device->GetLogical().CreateQueryPool({
+ pools.push_back(device.GetLogical().CreateQueryPool({
.sType = VK_STRUCTURE_TYPE_QUERY_POOL_CREATE_INFO,
.pNext = nullptr,
.flags = 0,
@@ -68,30 +66,39 @@ void QueryPool::Reserve(std::pair<VkQueryPool, u32> query) {
usage[pool_index * GROW_STEP + static_cast<std::ptrdiff_t>(query.second)] = false;
}
-VKQueryCache::VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
+VKQueryCache::VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
const VKDevice& device, VKScheduler& scheduler)
- : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
- QueryPool>{system, rasterizer},
- device{device}, scheduler{scheduler} {
- for (std::size_t i = 0; i < static_cast<std::size_t>(VideoCore::NumQueryTypes); ++i) {
- query_pools[i].Initialize(device, static_cast<VideoCore::QueryType>(i));
+ : VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream,
+ HostCounter>{rasterizer, maxwell3d, gpu_memory},
+ device{device}, scheduler{scheduler}, query_pools{
+ QueryPool{device, scheduler,
+ QueryType::SamplesPassed},
+ } {}
+
+VKQueryCache::~VKQueryCache() {
+ // TODO(Rodrigo): This is a hack to destroy all HostCounter instances before the base class
+ // destructor is called. The query cache should be redesigned to have a proper ownership model
+ // instead of using shared pointers.
+ for (size_t query_type = 0; query_type < VideoCore::NumQueryTypes; ++query_type) {
+ auto& stream = Stream(static_cast<QueryType>(query_type));
+ stream.Update(false);
+ stream.Reset();
}
}
-VKQueryCache::~VKQueryCache() = default;
-
-std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(VideoCore::QueryType type) {
- return query_pools[static_cast<std::size_t>(type)].Commit(scheduler.GetFence());
+std::pair<VkQueryPool, u32> VKQueryCache::AllocateQuery(QueryType type) {
+ return query_pools[static_cast<std::size_t>(type)].Commit();
}
-void VKQueryCache::Reserve(VideoCore::QueryType type, std::pair<VkQueryPool, u32> query) {
+void VKQueryCache::Reserve(QueryType type, std::pair<VkQueryPool, u32> query) {
query_pools[static_cast<std::size_t>(type)].Reserve(query);
}
HostCounter::HostCounter(VKQueryCache& cache, std::shared_ptr<HostCounter> dependency,
- VideoCore::QueryType type)
+ QueryType type)
: VideoCommon::HostCounterBase<VKQueryCache, HostCounter>{std::move(dependency)}, cache{cache},
- type{type}, query{cache.AllocateQuery(type)}, ticks{cache.Scheduler().Ticks()} {
+ type{type}, query{cache.AllocateQuery(type)}, tick{cache.Scheduler().CurrentTick()} {
const vk::Device* logical = &cache.Device().GetLogical();
cache.Scheduler().Record([logical, query = query](vk::CommandBuffer cmdbuf) {
logical->ResetQueryPoolEXT(query.first, query.second, 1);
@@ -109,7 +116,7 @@ void HostCounter::EndQuery() {
}
u64 HostCounter::BlockingQuery() const {
- if (ticks >= cache.Scheduler().Ticks()) {
+ if (tick >= cache.Scheduler().CurrentTick()) {
cache.Scheduler().Flush();
}
u64 data;
diff --git a/src/video_core/renderer_vulkan/vk_query_cache.h b/src/video_core/renderer_vulkan/vk_query_cache.h
index 40119e6d3..2e57fb75d 100644
--- a/src/video_core/renderer_vulkan/vk_query_cache.h
+++ b/src/video_core/renderer_vulkan/vk_query_cache.h
@@ -11,7 +11,7 @@
#include "common/common_types.h"
#include "video_core/query_cache.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
+#include "video_core/renderer_vulkan/vk_resource_pool.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace VideoCore {
@@ -28,14 +28,12 @@ class VKScheduler;
using CounterStream = VideoCommon::CounterStreamBase<VKQueryCache, HostCounter>;
-class QueryPool final : public VKFencedPool {
+class QueryPool final : public ResourcePool {
public:
- explicit QueryPool();
+ explicit QueryPool(const VKDevice& device, VKScheduler& scheduler, VideoCore::QueryType type);
~QueryPool() override;
- void Initialize(const VKDevice& device, VideoCore::QueryType type);
-
- std::pair<VkQueryPool, u32> Commit(VKFence& fence);
+ std::pair<VkQueryPool, u32> Commit();
void Reserve(std::pair<VkQueryPool, u32> query);
@@ -45,18 +43,18 @@ protected:
private:
static constexpr std::size_t GROW_STEP = 512;
- const VKDevice* device = nullptr;
- VideoCore::QueryType type = {};
+ const VKDevice& device;
+ const VideoCore::QueryType type;
std::vector<vk::QueryPool> pools;
std::vector<bool> usage;
};
class VKQueryCache final
- : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter,
- QueryPool> {
+ : public VideoCommon::QueryCacheBase<VKQueryCache, CachedQuery, CounterStream, HostCounter> {
public:
- explicit VKQueryCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
+ explicit VKQueryCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
const VKDevice& device, VKScheduler& scheduler);
~VKQueryCache();
@@ -75,6 +73,7 @@ public:
private:
const VKDevice& device;
VKScheduler& scheduler;
+ std::array<QueryPool, VideoCore::NumQueryTypes> query_pools;
};
class HostCounter final : public VideoCommon::HostCounterBase<VKQueryCache, HostCounter> {
@@ -91,7 +90,7 @@ private:
VKQueryCache& cache;
const VideoCore::QueryType type;
const std::pair<VkQueryPool, u32> query;
- const u64 ticks;
+ const u64 tick;
};
class CachedQuery : public VideoCommon::CachedQueryBase<HostCounter> {
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.cpp b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
index 936f76195..e0fb8693f 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.cpp
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.cpp
@@ -31,7 +31,6 @@
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_rasterizer.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_sampler_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -381,28 +380,28 @@ void RasterizerVulkan::DrawParameters::Draw(vk::CommandBuffer cmdbuf) const {
}
}
-RasterizerVulkan::RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& renderer,
- VKScreenInfo& screen_info, const VKDevice& device,
- VKResourceManager& resource_manager,
- VKMemoryManager& memory_manager, StateTracker& state_tracker,
- VKScheduler& scheduler)
- : RasterizerAccelerated{system.Memory()}, system{system}, render_window{renderer},
- screen_info{screen_info}, device{device}, resource_manager{resource_manager},
- memory_manager{memory_manager}, state_tracker{state_tracker}, scheduler{scheduler},
- staging_pool(device, memory_manager, scheduler), descriptor_pool(device),
- update_descriptor_queue(device, scheduler), renderpass_cache(device),
+RasterizerVulkan::RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu_,
+ Tegra::MemoryManager& gpu_memory_,
+ Core::Memory::Memory& cpu_memory, VKScreenInfo& screen_info_,
+ const VKDevice& device_, VKMemoryManager& memory_manager_,
+ StateTracker& state_tracker_, VKScheduler& scheduler_)
+ : RasterizerAccelerated(cpu_memory), gpu(gpu_), gpu_memory(gpu_memory_),
+ maxwell3d(gpu.Maxwell3D()), kepler_compute(gpu.KeplerCompute()), screen_info(screen_info_),
+ device(device_), memory_manager(memory_manager_), state_tracker(state_tracker_),
+ scheduler(scheduler_), staging_pool(device, memory_manager, scheduler),
+ descriptor_pool(device, scheduler_), update_descriptor_queue(device, scheduler),
+ renderpass_cache(device),
quad_array_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
quad_indexed_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
uint8_pass(device, scheduler, descriptor_pool, staging_pool, update_descriptor_queue),
- texture_cache(system, *this, device, resource_manager, memory_manager, scheduler,
- staging_pool),
- pipeline_cache(system, *this, device, scheduler, descriptor_pool, update_descriptor_queue,
- renderpass_cache),
- buffer_cache(*this, system, device, memory_manager, scheduler, staging_pool),
- sampler_cache(device),
- fence_manager(system, *this, device, scheduler, texture_cache, buffer_cache, query_cache),
- query_cache(system, *this, device, scheduler),
- wfi_event{device.GetLogical().CreateNewEvent()}, async_shaders{renderer} {
+ texture_cache(*this, maxwell3d, gpu_memory, device, memory_manager, scheduler, staging_pool),
+ pipeline_cache(*this, gpu, maxwell3d, kepler_compute, gpu_memory, device, scheduler,
+ descriptor_pool, update_descriptor_queue, renderpass_cache),
+ buffer_cache(*this, gpu_memory, cpu_memory, device, memory_manager, scheduler, staging_pool),
+ sampler_cache(device), query_cache(*this, maxwell3d, gpu_memory, device, scheduler),
+ fence_manager(*this, gpu, gpu_memory, texture_cache, buffer_cache, query_cache, device,
+ scheduler),
+ wfi_event(device.GetLogical().CreateEvent()), async_shaders(emu_window) {
scheduler.SetQueryCache(query_cache);
if (device.UseAsynchronousShaders()) {
async_shaders.AllocateWorkers();
@@ -414,15 +413,13 @@ RasterizerVulkan::~RasterizerVulkan() = default;
void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
MICROPROFILE_SCOPE(Vulkan_Drawing);
+ SCOPE_EXIT({ gpu.TickWork(); });
FlushWork();
query_cache.UpdateCounters();
- SCOPE_EXIT({ system.GPU().TickWork(); });
-
- const auto& gpu = system.GPU().Maxwell3D();
GraphicsPipelineCacheKey key;
- key.fixed_state.Fill(gpu.regs, device.IsExtExtendedDynamicStateSupported());
+ key.fixed_state.Fill(maxwell3d.regs, device.IsExtExtendedDynamicStateSupported());
buffer_cache.Map(CalculateGraphicsStreamBufferSize(is_indexed));
@@ -480,8 +477,7 @@ void RasterizerVulkan::Draw(bool is_indexed, bool is_instanced) {
void RasterizerVulkan::Clear() {
MICROPROFILE_SCOPE(Vulkan_Clearing);
- const auto& gpu = system.GPU().Maxwell3D();
- if (!system.GPU().Maxwell3D().ShouldExecute()) {
+ if (!maxwell3d.ShouldExecute()) {
return;
}
@@ -490,7 +486,7 @@ void RasterizerVulkan::Clear() {
query_cache.UpdateCounters();
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
const bool use_color = regs.clear_buffers.R || regs.clear_buffers.G || regs.clear_buffers.B ||
regs.clear_buffers.A;
const bool use_depth = regs.clear_buffers.Z;
@@ -559,7 +555,7 @@ void RasterizerVulkan::DispatchCompute(GPUVAddr code_addr) {
query_cache.UpdateCounters();
- const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
+ const auto& launch_desc = kepler_compute.launch_description;
auto& pipeline = pipeline_cache.GetComputePipeline({
.shader = code_addr,
.shared_memory_size = launch_desc.shared_alloc,
@@ -655,16 +651,14 @@ void RasterizerVulkan::SyncGuestHost() {
}
void RasterizerVulkan::SignalSemaphore(GPUVAddr addr, u32 value) {
- auto& gpu{system.GPU()};
if (!gpu.IsAsync()) {
- gpu.MemoryManager().Write<u32>(addr, value);
+ gpu_memory.Write<u32>(addr, value);
return;
}
fence_manager.SignalSemaphore(addr, value);
}
void RasterizerVulkan::SignalSyncPoint(u32 value) {
- auto& gpu{system.GPU()};
if (!gpu.IsAsync()) {
gpu.IncrementSyncPoint(value);
return;
@@ -673,7 +667,6 @@ void RasterizerVulkan::SignalSyncPoint(u32 value) {
}
void RasterizerVulkan::ReleaseFences() {
- auto& gpu{system.GPU()};
if (!gpu.IsAsync()) {
return;
}
@@ -751,10 +744,6 @@ bool RasterizerVulkan::AccelerateDisplay(const Tegra::FramebufferConfig& config,
return true;
}
-void RasterizerVulkan::SetupDirtyFlags() {
- state_tracker.Initialize();
-}
-
void RasterizerVulkan::FlushWork() {
static constexpr u32 DRAWS_TO_DISPATCH = 4096;
@@ -778,10 +767,9 @@ void RasterizerVulkan::FlushWork() {
RasterizerVulkan::Texceptions RasterizerVulkan::UpdateAttachments(bool is_clear) {
MICROPROFILE_SCOPE(Vulkan_RenderTargets);
- auto& maxwell3d = system.GPU().Maxwell3D();
- auto& dirty = maxwell3d.dirty.flags;
- auto& regs = maxwell3d.regs;
+ const auto& regs = maxwell3d.regs;
+ auto& dirty = maxwell3d.dirty.flags;
const bool update_rendertargets = dirty[VideoCommon::Dirty::RenderTargets];
dirty[VideoCommon::Dirty::RenderTargets] = false;
@@ -844,7 +832,7 @@ std::tuple<VkFramebuffer, VkExtent2D> RasterizerVulkan::ConfigureFramebuffers(
return true;
};
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
const std::size_t num_attachments = static_cast<std::size_t>(regs.rt_control.count);
for (std::size_t index = 0; index < num_attachments; ++index) {
if (try_push(color_attachments[index])) {
@@ -880,13 +868,12 @@ RasterizerVulkan::DrawParameters RasterizerVulkan::SetupGeometry(FixedPipelineSt
bool is_instanced) {
MICROPROFILE_SCOPE(Vulkan_Geometry);
- const auto& gpu = system.GPU().Maxwell3D();
- const auto& regs = gpu.regs;
+ const auto& regs = maxwell3d.regs;
SetupVertexArrays(buffer_bindings);
const u32 base_instance = regs.vb_base_instance;
- const u32 num_instances = is_instanced ? gpu.mme_draw.instance_count : 1;
+ const u32 num_instances = is_instanced ? maxwell3d.mme_draw.instance_count : 1;
const u32 base_vertex = is_indexed ? regs.vb_element_base : regs.vertex_buffer.first;
const u32 num_vertices = is_indexed ? regs.index_array.count : regs.vertex_buffer.count;
@@ -947,7 +934,7 @@ void RasterizerVulkan::SetupImageTransitions(
}
void RasterizerVulkan::UpdateDynamicStates() {
- auto& regs = system.GPU().Maxwell3D().regs;
+ auto& regs = maxwell3d.regs;
UpdateViewportsState(regs);
UpdateScissorsState(regs);
UpdateDepthBias(regs);
@@ -961,14 +948,13 @@ void RasterizerVulkan::UpdateDynamicStates() {
UpdateDepthWriteEnable(regs);
UpdateDepthCompareOp(regs);
UpdateFrontFace(regs);
- UpdatePrimitiveTopology(regs);
UpdateStencilOp(regs);
UpdateStencilTestEnable(regs);
}
}
void RasterizerVulkan::BeginTransformFeedback() {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
if (regs.tfb_enabled == 0) {
return;
}
@@ -1000,7 +986,7 @@ void RasterizerVulkan::BeginTransformFeedback() {
}
void RasterizerVulkan::EndTransformFeedback() {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
if (regs.tfb_enabled == 0) {
return;
}
@@ -1013,7 +999,7 @@ void RasterizerVulkan::EndTransformFeedback() {
}
void RasterizerVulkan::SetupVertexArrays(BufferBindings& buffer_bindings) {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
for (std::size_t index = 0; index < Maxwell::NumVertexArrays; ++index) {
const auto& vertex_array = regs.vertex_array[index];
@@ -1039,7 +1025,7 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar
if (params.num_vertices == 0) {
return;
}
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
switch (regs.draw.topology) {
case Maxwell::PrimitiveTopology::Quads: {
if (!params.is_indexed) {
@@ -1087,8 +1073,7 @@ void RasterizerVulkan::SetupIndexBuffer(BufferBindings& buffer_bindings, DrawPar
void RasterizerVulkan::SetupGraphicsConstBuffers(const ShaderEntries& entries, std::size_t stage) {
MICROPROFILE_SCOPE(Vulkan_ConstBuffers);
- const auto& gpu = system.GPU().Maxwell3D();
- const auto& shader_stage = gpu.state.shader_stages[stage];
+ const auto& shader_stage = maxwell3d.state.shader_stages[stage];
for (const auto& entry : entries.const_buffers) {
SetupConstBuffer(entry, shader_stage.const_buffers[entry.GetIndex()]);
}
@@ -1096,8 +1081,7 @@ void RasterizerVulkan::SetupGraphicsConstBuffers(const ShaderEntries& entries, s
void RasterizerVulkan::SetupGraphicsGlobalBuffers(const ShaderEntries& entries, std::size_t stage) {
MICROPROFILE_SCOPE(Vulkan_GlobalBuffers);
- auto& gpu{system.GPU()};
- const auto cbufs{gpu.Maxwell3D().state.shader_stages[stage]};
+ const auto& cbufs{maxwell3d.state.shader_stages[stage]};
for (const auto& entry : entries.global_buffers) {
const auto addr = cbufs.const_buffers[entry.GetCbufIndex()].address + entry.GetCbufOffset();
@@ -1107,19 +1091,17 @@ void RasterizerVulkan::SetupGraphicsGlobalBuffers(const ShaderEntries& entries,
void RasterizerVulkan::SetupGraphicsUniformTexels(const ShaderEntries& entries, std::size_t stage) {
MICROPROFILE_SCOPE(Vulkan_Textures);
- const auto& gpu = system.GPU().Maxwell3D();
for (const auto& entry : entries.uniform_texels) {
- const auto image = GetTextureInfo(gpu, entry, stage).tic;
+ const auto image = GetTextureInfo(maxwell3d, entry, stage).tic;
SetupUniformTexels(image, entry);
}
}
void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std::size_t stage) {
MICROPROFILE_SCOPE(Vulkan_Textures);
- const auto& gpu = system.GPU().Maxwell3D();
for (const auto& entry : entries.samplers) {
for (std::size_t i = 0; i < entry.size; ++i) {
- const auto texture = GetTextureInfo(gpu, entry, stage, i);
+ const auto texture = GetTextureInfo(maxwell3d, entry, stage, i);
SetupTexture(texture, entry);
}
}
@@ -1127,25 +1109,23 @@ void RasterizerVulkan::SetupGraphicsTextures(const ShaderEntries& entries, std::
void RasterizerVulkan::SetupGraphicsStorageTexels(const ShaderEntries& entries, std::size_t stage) {
MICROPROFILE_SCOPE(Vulkan_Textures);
- const auto& gpu = system.GPU().Maxwell3D();
for (const auto& entry : entries.storage_texels) {
- const auto image = GetTextureInfo(gpu, entry, stage).tic;
+ const auto image = GetTextureInfo(maxwell3d, entry, stage).tic;
SetupStorageTexel(image, entry);
}
}
void RasterizerVulkan::SetupGraphicsImages(const ShaderEntries& entries, std::size_t stage) {
MICROPROFILE_SCOPE(Vulkan_Images);
- const auto& gpu = system.GPU().Maxwell3D();
for (const auto& entry : entries.images) {
- const auto tic = GetTextureInfo(gpu, entry, stage).tic;
+ const auto tic = GetTextureInfo(maxwell3d, entry, stage).tic;
SetupImage(tic, entry);
}
}
void RasterizerVulkan::SetupComputeConstBuffers(const ShaderEntries& entries) {
MICROPROFILE_SCOPE(Vulkan_ConstBuffers);
- const auto& launch_desc = system.GPU().KeplerCompute().launch_description;
+ const auto& launch_desc = kepler_compute.launch_description;
for (const auto& entry : entries.const_buffers) {
const auto& config = launch_desc.const_buffer_config[entry.GetIndex()];
const std::bitset<8> mask = launch_desc.const_buffer_enable_mask.Value();
@@ -1159,7 +1139,7 @@ void RasterizerVulkan::SetupComputeConstBuffers(const ShaderEntries& entries) {
void RasterizerVulkan::SetupComputeGlobalBuffers(const ShaderEntries& entries) {
MICROPROFILE_SCOPE(Vulkan_GlobalBuffers);
- const auto cbufs{system.GPU().KeplerCompute().launch_description.const_buffer_config};
+ const auto& cbufs{kepler_compute.launch_description.const_buffer_config};
for (const auto& entry : entries.global_buffers) {
const auto addr{cbufs[entry.GetCbufIndex()].Address() + entry.GetCbufOffset()};
SetupGlobalBuffer(entry, addr);
@@ -1168,19 +1148,17 @@ void RasterizerVulkan::SetupComputeGlobalBuffers(const ShaderEntries& entries) {
void RasterizerVulkan::SetupComputeUniformTexels(const ShaderEntries& entries) {
MICROPROFILE_SCOPE(Vulkan_Textures);
- const auto& gpu = system.GPU().KeplerCompute();
for (const auto& entry : entries.uniform_texels) {
- const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic;
+ const auto image = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex).tic;
SetupUniformTexels(image, entry);
}
}
void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) {
MICROPROFILE_SCOPE(Vulkan_Textures);
- const auto& gpu = system.GPU().KeplerCompute();
for (const auto& entry : entries.samplers) {
for (std::size_t i = 0; i < entry.size; ++i) {
- const auto texture = GetTextureInfo(gpu, entry, ComputeShaderIndex, i);
+ const auto texture = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex, i);
SetupTexture(texture, entry);
}
}
@@ -1188,18 +1166,16 @@ void RasterizerVulkan::SetupComputeTextures(const ShaderEntries& entries) {
void RasterizerVulkan::SetupComputeStorageTexels(const ShaderEntries& entries) {
MICROPROFILE_SCOPE(Vulkan_Textures);
- const auto& gpu = system.GPU().KeplerCompute();
for (const auto& entry : entries.storage_texels) {
- const auto image = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic;
+ const auto image = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex).tic;
SetupStorageTexel(image, entry);
}
}
void RasterizerVulkan::SetupComputeImages(const ShaderEntries& entries) {
MICROPROFILE_SCOPE(Vulkan_Images);
- const auto& gpu = system.GPU().KeplerCompute();
for (const auto& entry : entries.images) {
- const auto tic = GetTextureInfo(gpu, entry, ComputeShaderIndex).tic;
+ const auto tic = GetTextureInfo(kepler_compute, entry, ComputeShaderIndex).tic;
SetupImage(tic, entry);
}
}
@@ -1223,9 +1199,8 @@ void RasterizerVulkan::SetupConstBuffer(const ConstBufferEntry& entry,
}
void RasterizerVulkan::SetupGlobalBuffer(const GlobalBufferEntry& entry, GPUVAddr address) {
- auto& memory_manager{system.GPU().MemoryManager()};
- const auto actual_addr = memory_manager.Read<u64>(address);
- const auto size = memory_manager.Read<u32>(address + 8);
+ const u64 actual_addr = gpu_memory.Read<u64>(address);
+ const u32 size = gpu_memory.Read<u32>(address + 8);
if (size == 0) {
// Sometimes global memory pointers don't have a proper size. Upload a dummy entry
@@ -1442,16 +1417,6 @@ void RasterizerVulkan::UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs) {
[front_face](vk::CommandBuffer cmdbuf) { cmdbuf.SetFrontFaceEXT(front_face); });
}
-void RasterizerVulkan::UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs) {
- if (!state_tracker.TouchPrimitiveTopology()) {
- return;
- }
- const Maxwell::PrimitiveTopology primitive_topology = regs.draw.topology.Value();
- scheduler.Record([this, primitive_topology](vk::CommandBuffer cmdbuf) {
- cmdbuf.SetPrimitiveTopologyEXT(MaxwellToVK::PrimitiveTopology(device, primitive_topology));
- });
-}
-
void RasterizerVulkan::UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs) {
if (!state_tracker.TouchStencilOp()) {
return;
@@ -1508,7 +1473,7 @@ std::size_t RasterizerVulkan::CalculateComputeStreamBufferSize() const {
}
std::size_t RasterizerVulkan::CalculateVertexArraysSize() const {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
std::size_t size = 0;
for (u32 index = 0; index < Maxwell::NumVertexArrays; ++index) {
@@ -1523,9 +1488,8 @@ std::size_t RasterizerVulkan::CalculateVertexArraysSize() const {
}
std::size_t RasterizerVulkan::CalculateIndexBufferSize() const {
- const auto& regs = system.GPU().Maxwell3D().regs;
- return static_cast<std::size_t>(regs.index_array.count) *
- static_cast<std::size_t>(regs.index_array.FormatSizeInBytes());
+ return static_cast<std::size_t>(maxwell3d.regs.index_array.count) *
+ static_cast<std::size_t>(maxwell3d.regs.index_array.FormatSizeInBytes());
}
std::size_t RasterizerVulkan::CalculateConstBufferSize(
@@ -1540,7 +1504,7 @@ std::size_t RasterizerVulkan::CalculateConstBufferSize(
}
RenderPassParams RasterizerVulkan::GetRenderPassParams(Texceptions texceptions) const {
- const auto& regs = system.GPU().Maxwell3D().regs;
+ const auto& regs = maxwell3d.regs;
const std::size_t num_attachments = static_cast<std::size_t>(regs.rt_control.count);
RenderPassParams params;
diff --git a/src/video_core/renderer_vulkan/vk_rasterizer.h b/src/video_core/renderer_vulkan/vk_rasterizer.h
index f640ba649..237e51fa4 100644
--- a/src/video_core/renderer_vulkan/vk_rasterizer.h
+++ b/src/video_core/renderer_vulkan/vk_rasterizer.h
@@ -25,7 +25,6 @@
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
#include "video_core/renderer_vulkan/vk_renderpass_cache.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_sampler_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
@@ -106,10 +105,11 @@ struct ImageView {
class RasterizerVulkan final : public VideoCore::RasterizerAccelerated {
public:
- explicit RasterizerVulkan(Core::System& system, Core::Frontend::EmuWindow& render_window,
+ explicit RasterizerVulkan(Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
+ Tegra::MemoryManager& gpu_memory, Core::Memory::Memory& cpu_memory,
VKScreenInfo& screen_info, const VKDevice& device,
- VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
- StateTracker& state_tracker, VKScheduler& scheduler);
+ VKMemoryManager& memory_manager, StateTracker& state_tracker,
+ VKScheduler& scheduler);
~RasterizerVulkan() override;
void Draw(bool is_indexed, bool is_instanced) override;
@@ -135,7 +135,6 @@ public:
const Tegra::Engines::Fermi2D::Config& copy_config) override;
bool AccelerateDisplay(const Tegra::FramebufferConfig& config, VAddr framebuffer_addr,
u32 pixel_stride) override;
- void SetupDirtyFlags() override;
VideoCommon::Shader::AsyncShaders& GetAsyncShaders() {
return async_shaders;
@@ -260,7 +259,6 @@ private:
void UpdateDepthWriteEnable(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateDepthCompareOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateFrontFace(Tegra::Engines::Maxwell3D::Regs& regs);
- void UpdatePrimitiveTopology(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilOp(Tegra::Engines::Maxwell3D::Regs& regs);
void UpdateStencilTestEnable(Tegra::Engines::Maxwell3D::Regs& regs);
@@ -279,11 +277,13 @@ private:
VkBuffer DefaultBuffer();
- Core::System& system;
- Core::Frontend::EmuWindow& render_window;
+ Tegra::GPU& gpu;
+ Tegra::MemoryManager& gpu_memory;
+ Tegra::Engines::Maxwell3D& maxwell3d;
+ Tegra::Engines::KeplerCompute& kepler_compute;
+
VKScreenInfo& screen_info;
const VKDevice& device;
- VKResourceManager& resource_manager;
VKMemoryManager& memory_manager;
StateTracker& state_tracker;
VKScheduler& scheduler;
@@ -300,8 +300,8 @@ private:
VKPipelineCache pipeline_cache;
VKBufferCache buffer_cache;
VKSamplerCache sampler_cache;
- VKFenceManager fence_manager;
VKQueryCache query_cache;
+ VKFenceManager fence_manager;
vk::Buffer default_buffer;
VKMemoryCommit default_buffer_commit;
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.cpp b/src/video_core/renderer_vulkan/vk_resource_manager.cpp
deleted file mode 100644
index f19330a36..000000000
--- a/src/video_core/renderer_vulkan/vk_resource_manager.cpp
+++ /dev/null
@@ -1,311 +0,0 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <algorithm>
-#include <optional>
-#include "common/assert.h"
-#include "common/logging/log.h"
-#include "video_core/renderer_vulkan/vk_device.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
-#include "video_core/renderer_vulkan/wrapper.h"
-
-namespace Vulkan {
-
-namespace {
-
-// TODO(Rodrigo): Fine tune these numbers.
-constexpr std::size_t COMMAND_BUFFER_POOL_SIZE = 0x1000;
-constexpr std::size_t FENCES_GROW_STEP = 0x40;
-
-constexpr VkFenceCreateInfo BuildFenceCreateInfo() {
- return {
- .sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO,
- .pNext = nullptr,
- .flags = 0,
- };
-}
-
-} // Anonymous namespace
-
-class CommandBufferPool final : public VKFencedPool {
-public:
- explicit CommandBufferPool(const VKDevice& device)
- : VKFencedPool(COMMAND_BUFFER_POOL_SIZE), device{device} {}
-
- void Allocate(std::size_t begin, std::size_t end) override {
- // Command buffers are going to be commited, recorded, executed every single usage cycle.
- // They are also going to be reseted when commited.
- Pool& pool = pools.emplace_back();
- pool.handle = device.GetLogical().CreateCommandPool({
- .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
- .pNext = nullptr,
- .flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT |
- VK_COMMAND_POOL_CREATE_RESET_COMMAND_BUFFER_BIT,
- .queueFamilyIndex = device.GetGraphicsFamily(),
- });
- pool.cmdbufs = pool.handle.Allocate(COMMAND_BUFFER_POOL_SIZE);
- }
-
- VkCommandBuffer Commit(VKFence& fence) {
- const std::size_t index = CommitResource(fence);
- const auto pool_index = index / COMMAND_BUFFER_POOL_SIZE;
- const auto sub_index = index % COMMAND_BUFFER_POOL_SIZE;
- return pools[pool_index].cmdbufs[sub_index];
- }
-
-private:
- struct Pool {
- vk::CommandPool handle;
- vk::CommandBuffers cmdbufs;
- };
-
- const VKDevice& device;
- std::vector<Pool> pools;
-};
-
-VKResource::VKResource() = default;
-
-VKResource::~VKResource() = default;
-
-VKFence::VKFence(const VKDevice& device)
- : device{device}, handle{device.GetLogical().CreateFence(BuildFenceCreateInfo())} {}
-
-VKFence::~VKFence() = default;
-
-void VKFence::Wait() {
- switch (const VkResult result = handle.Wait()) {
- case VK_SUCCESS:
- return;
- case VK_ERROR_DEVICE_LOST:
- device.ReportLoss();
- [[fallthrough]];
- default:
- throw vk::Exception(result);
- }
-}
-
-void VKFence::Release() {
- ASSERT(is_owned);
- is_owned = false;
-}
-
-void VKFence::Commit() {
- is_owned = true;
- is_used = true;
-}
-
-bool VKFence::Tick(bool gpu_wait, bool owner_wait) {
- if (!is_used) {
- // If a fence is not used it's always free.
- return true;
- }
- if (is_owned && !owner_wait) {
- // The fence is still being owned (Release has not been called) and ownership wait has
- // not been asked.
- return false;
- }
-
- if (gpu_wait) {
- // Wait for the fence if it has been requested.
- (void)handle.Wait();
- } else {
- if (handle.GetStatus() != VK_SUCCESS) {
- // Vulkan fence is not ready, not much it can do here
- return false;
- }
- }
-
- // Broadcast resources their free state.
- for (auto* resource : protected_resources) {
- resource->OnFenceRemoval(this);
- }
- protected_resources.clear();
-
- // Prepare fence for reusage.
- handle.Reset();
- is_used = false;
- return true;
-}
-
-void VKFence::Protect(VKResource* resource) {
- protected_resources.push_back(resource);
-}
-
-void VKFence::Unprotect(VKResource* resource) {
- const auto it = std::find(protected_resources.begin(), protected_resources.end(), resource);
- ASSERT(it != protected_resources.end());
-
- resource->OnFenceRemoval(this);
- protected_resources.erase(it);
-}
-
-void VKFence::RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept {
- std::replace(std::begin(protected_resources), std::end(protected_resources), old_resource,
- new_resource);
-}
-
-VKFenceWatch::VKFenceWatch() = default;
-
-VKFenceWatch::VKFenceWatch(VKFence& initial_fence) {
- Watch(initial_fence);
-}
-
-VKFenceWatch::VKFenceWatch(VKFenceWatch&& rhs) noexcept {
- fence = std::exchange(rhs.fence, nullptr);
- if (fence) {
- fence->RedirectProtection(&rhs, this);
- }
-}
-
-VKFenceWatch& VKFenceWatch::operator=(VKFenceWatch&& rhs) noexcept {
- fence = std::exchange(rhs.fence, nullptr);
- if (fence) {
- fence->RedirectProtection(&rhs, this);
- }
- return *this;
-}
-
-VKFenceWatch::~VKFenceWatch() {
- if (fence) {
- fence->Unprotect(this);
- }
-}
-
-void VKFenceWatch::Wait() {
- if (fence == nullptr) {
- return;
- }
- fence->Wait();
- fence->Unprotect(this);
-}
-
-void VKFenceWatch::Watch(VKFence& new_fence) {
- Wait();
- fence = &new_fence;
- fence->Protect(this);
-}
-
-bool VKFenceWatch::TryWatch(VKFence& new_fence) {
- if (fence) {
- return false;
- }
- fence = &new_fence;
- fence->Protect(this);
- return true;
-}
-
-void VKFenceWatch::OnFenceRemoval(VKFence* signaling_fence) {
- ASSERT_MSG(signaling_fence == fence, "Removing the wrong fence");
- fence = nullptr;
-}
-
-VKFencedPool::VKFencedPool(std::size_t grow_step) : grow_step{grow_step} {}
-
-VKFencedPool::~VKFencedPool() = default;
-
-std::size_t VKFencedPool::CommitResource(VKFence& fence) {
- const auto Search = [&](std::size_t begin, std::size_t end) -> std::optional<std::size_t> {
- for (std::size_t iterator = begin; iterator < end; ++iterator) {
- if (watches[iterator]->TryWatch(fence)) {
- // The resource is now being watched, a free resource was successfully found.
- return iterator;
- }
- }
- return {};
- };
- // Try to find a free resource from the hinted position to the end.
- auto found = Search(free_iterator, watches.size());
- if (!found) {
- // Search from beginning to the hinted position.
- found = Search(0, free_iterator);
- if (!found) {
- // Both searches failed, the pool is full; handle it.
- const std::size_t free_resource = ManageOverflow();
-
- // Watch will wait for the resource to be free.
- watches[free_resource]->Watch(fence);
- found = free_resource;
- }
- }
- // Free iterator is hinted to the resource after the one that's been commited.
- free_iterator = (*found + 1) % watches.size();
- return *found;
-}
-
-std::size_t VKFencedPool::ManageOverflow() {
- const std::size_t old_capacity = watches.size();
- Grow();
-
- // The last entry is guaranted to be free, since it's the first element of the freshly
- // allocated resources.
- return old_capacity;
-}
-
-void VKFencedPool::Grow() {
- const std::size_t old_capacity = watches.size();
- watches.resize(old_capacity + grow_step);
- std::generate(watches.begin() + old_capacity, watches.end(),
- []() { return std::make_unique<VKFenceWatch>(); });
- Allocate(old_capacity, old_capacity + grow_step);
-}
-
-VKResourceManager::VKResourceManager(const VKDevice& device) : device{device} {
- GrowFences(FENCES_GROW_STEP);
- command_buffer_pool = std::make_unique<CommandBufferPool>(device);
-}
-
-VKResourceManager::~VKResourceManager() = default;
-
-VKFence& VKResourceManager::CommitFence() {
- const auto StepFences = [&](bool gpu_wait, bool owner_wait) -> VKFence* {
- const auto Tick = [=](auto& fence) { return fence->Tick(gpu_wait, owner_wait); };
- const auto hinted = fences.begin() + fences_iterator;
-
- auto it = std::find_if(hinted, fences.end(), Tick);
- if (it == fences.end()) {
- it = std::find_if(fences.begin(), hinted, Tick);
- if (it == hinted) {
- return nullptr;
- }
- }
- fences_iterator = std::distance(fences.begin(), it) + 1;
- if (fences_iterator >= fences.size())
- fences_iterator = 0;
-
- auto& fence = *it;
- fence->Commit();
- return fence.get();
- };
-
- VKFence* found_fence = StepFences(false, false);
- if (!found_fence) {
- // Try again, this time waiting.
- found_fence = StepFences(true, false);
-
- if (!found_fence) {
- // Allocate new fences and try again.
- LOG_INFO(Render_Vulkan, "Allocating new fences {} -> {}", fences.size(),
- fences.size() + FENCES_GROW_STEP);
-
- GrowFences(FENCES_GROW_STEP);
- found_fence = StepFences(true, false);
- ASSERT(found_fence != nullptr);
- }
- }
- return *found_fence;
-}
-
-VkCommandBuffer VKResourceManager::CommitCommandBuffer(VKFence& fence) {
- return command_buffer_pool->Commit(fence);
-}
-
-void VKResourceManager::GrowFences(std::size_t new_fences_count) {
- const std::size_t previous_size = fences.size();
- fences.resize(previous_size + new_fences_count);
-
- std::generate(fences.begin() + previous_size, fences.end(),
- [this] { return std::make_unique<VKFence>(device); });
-}
-
-} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_manager.h b/src/video_core/renderer_vulkan/vk_resource_manager.h
deleted file mode 100644
index f683d2276..000000000
--- a/src/video_core/renderer_vulkan/vk_resource_manager.h
+++ /dev/null
@@ -1,196 +0,0 @@
-// Copyright 2018 yuzu Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <cstddef>
-#include <memory>
-#include <vector>
-#include "video_core/renderer_vulkan/wrapper.h"
-
-namespace Vulkan {
-
-class VKDevice;
-class VKFence;
-class VKResourceManager;
-
-class CommandBufferPool;
-
-/// Interface for a Vulkan resource
-class VKResource {
-public:
- explicit VKResource();
- virtual ~VKResource();
-
- /**
- * Signals the object that an owning fence has been signaled.
- * @param signaling_fence Fence that signals its usage end.
- */
- virtual void OnFenceRemoval(VKFence* signaling_fence) = 0;
-};
-
-/**
- * Fences take ownership of objects, protecting them from GPU-side or driver-side concurrent access.
- * They must be commited from the resource manager. Their usage flow is: commit the fence from the
- * resource manager, protect resources with it and use them, send the fence to an execution queue
- * and Wait for it if needed and then call Release. Used resources will automatically be signaled
- * when they are free to be reused.
- * @brief Protects resources for concurrent usage and signals its release.
- */
-class VKFence {
- friend class VKResourceManager;
-
-public:
- explicit VKFence(const VKDevice& device);
- ~VKFence();
-
- /**
- * Waits for the fence to be signaled.
- * @warning You must have ownership of the fence and it has to be previously sent to a queue to
- * call this function.
- */
- void Wait();
-
- /**
- * Releases ownership of the fence. Pass after it has been sent to an execution queue.
- * Unmanaged usage of the fence after the call will result in undefined behavior because it may
- * be being used for something else.
- */
- void Release();
-
- /// Protects a resource with this fence.
- void Protect(VKResource* resource);
-
- /// Removes protection for a resource.
- void Unprotect(VKResource* resource);
-
- /// Redirects one protected resource to a new address.
- void RedirectProtection(VKResource* old_resource, VKResource* new_resource) noexcept;
-
- /// Retreives the fence.
- operator VkFence() const {
- return *handle;
- }
-
-private:
- /// Take ownership of the fence.
- void Commit();
-
- /**
- * Updates the fence status.
- * @warning Waiting for the owner might soft lock the execution.
- * @param gpu_wait Wait for the fence to be signaled by the driver.
- * @param owner_wait Wait for the owner to signal its freedom.
- * @returns True if the fence is free. Waiting for gpu and owner will always return true.
- */
- bool Tick(bool gpu_wait, bool owner_wait);
-
- const VKDevice& device; ///< Device handler
- vk::Fence handle; ///< Vulkan fence
- std::vector<VKResource*> protected_resources; ///< List of resources protected by this fence
- bool is_owned = false; ///< The fence has been commited but not released yet.
- bool is_used = false; ///< The fence has been commited but it has not been checked to be free.
-};
-
-/**
- * A fence watch is used to keep track of the usage of a fence and protect a resource or set of
- * resources without having to inherit VKResource from their handlers.
- */
-class VKFenceWatch final : public VKResource {
-public:
- explicit VKFenceWatch();
- VKFenceWatch(VKFence& initial_fence);
- VKFenceWatch(VKFenceWatch&&) noexcept;
- VKFenceWatch(const VKFenceWatch&) = delete;
- ~VKFenceWatch() override;
-
- VKFenceWatch& operator=(VKFenceWatch&&) noexcept;
-
- /// Waits for the fence to be released.
- void Wait();
-
- /**
- * Waits for a previous fence and watches a new one.
- * @param new_fence New fence to wait to.
- */
- void Watch(VKFence& new_fence);
-
- /**
- * Checks if it's currently being watched and starts watching it if it's available.
- * @returns True if a watch has started, false if it's being watched.
- */
- bool TryWatch(VKFence& new_fence);
-
- void OnFenceRemoval(VKFence* signaling_fence) override;
-
- /**
- * Do not use it paired with Watch. Use TryWatch instead.
- * Returns true when the watch is free.
- */
- bool IsUsed() const {
- return fence != nullptr;
- }
-
-private:
- VKFence* fence{}; ///< Fence watching this resource. nullptr when the watch is free.
-};
-
-/**
- * Handles a pool of resources protected by fences. Manages resource overflow allocating more
- * resources.
- */
-class VKFencedPool {
-public:
- explicit VKFencedPool(std::size_t grow_step);
- virtual ~VKFencedPool();
-
-protected:
- /**
- * Commits a free resource and protects it with a fence. It may allocate new resources.
- * @param fence Fence that protects the commited resource.
- * @returns Index of the resource commited.
- */
- std::size_t CommitResource(VKFence& fence);
-
- /// Called when a chunk of resources have to be allocated.
- virtual void Allocate(std::size_t begin, std::size_t end) = 0;
-
-private:
- /// Manages pool overflow allocating new resources.
- std::size_t ManageOverflow();
-
- /// Allocates a new page of resources.
- void Grow();
-
- std::size_t grow_step = 0; ///< Number of new resources created after an overflow
- std::size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
- std::vector<std::unique_ptr<VKFenceWatch>> watches; ///< Set of watched resources
-};
-
-/**
- * The resource manager handles all resources that can be protected with a fence avoiding
- * driver-side or GPU-side concurrent usage. Usage is documented in VKFence.
- */
-class VKResourceManager final {
-public:
- explicit VKResourceManager(const VKDevice& device);
- ~VKResourceManager();
-
- /// Commits a fence. It has to be sent to a queue and released.
- VKFence& CommitFence();
-
- /// Commits an unused command buffer and protects it with a fence.
- VkCommandBuffer CommitCommandBuffer(VKFence& fence);
-
-private:
- /// Allocates new fences.
- void GrowFences(std::size_t new_fences_count);
-
- const VKDevice& device; ///< Device handler.
- std::size_t fences_iterator = 0; ///< Index where a free fence is likely to be found.
- std::vector<std::unique_ptr<VKFence>> fences; ///< Pool of fences.
- std::unique_ptr<CommandBufferPool> command_buffer_pool; ///< Pool of command buffers.
-};
-
-} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.cpp b/src/video_core/renderer_vulkan/vk_resource_pool.cpp
new file mode 100644
index 000000000..ee274ac59
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_resource_pool.cpp
@@ -0,0 +1,63 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <optional>
+
+#include "video_core/renderer_vulkan/vk_master_semaphore.h"
+#include "video_core/renderer_vulkan/vk_resource_pool.h"
+
+namespace Vulkan {
+
+ResourcePool::ResourcePool(MasterSemaphore& master_semaphore_, size_t grow_step_)
+ : master_semaphore{master_semaphore_}, grow_step{grow_step_} {}
+
+ResourcePool::~ResourcePool() = default;
+
+size_t ResourcePool::CommitResource() {
+ // Refresh semaphore to query updated results
+ master_semaphore.Refresh();
+
+ const auto search = [this](size_t begin, size_t end) -> std::optional<size_t> {
+ for (size_t iterator = begin; iterator < end; ++iterator) {
+ if (master_semaphore.IsFree(ticks[iterator])) {
+ ticks[iterator] = master_semaphore.CurrentTick();
+ return iterator;
+ }
+ }
+ return {};
+ };
+ // Try to find a free resource from the hinted position to the end.
+ auto found = search(free_iterator, ticks.size());
+ if (!found) {
+ // Search from beginning to the hinted position.
+ found = search(0, free_iterator);
+ if (!found) {
+ // Both searches failed, the pool is full; handle it.
+ const size_t free_resource = ManageOverflow();
+
+ ticks[free_resource] = master_semaphore.CurrentTick();
+ found = free_resource;
+ }
+ }
+ // Free iterator is hinted to the resource after the one that's been commited.
+ free_iterator = (*found + 1) % ticks.size();
+ return *found;
+}
+
+size_t ResourcePool::ManageOverflow() {
+ const size_t old_capacity = ticks.size();
+ Grow();
+
+ // The last entry is guaranted to be free, since it's the first element of the freshly
+ // allocated resources.
+ return old_capacity;
+}
+
+void ResourcePool::Grow() {
+ const size_t old_capacity = ticks.size();
+ ticks.resize(old_capacity + grow_step);
+ Allocate(old_capacity, old_capacity + grow_step);
+}
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_resource_pool.h b/src/video_core/renderer_vulkan/vk_resource_pool.h
new file mode 100644
index 000000000..a018c7ec2
--- /dev/null
+++ b/src/video_core/renderer_vulkan/vk_resource_pool.h
@@ -0,0 +1,43 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <vector>
+
+#include "common/common_types.h"
+
+namespace Vulkan {
+
+class MasterSemaphore;
+
+/**
+ * Handles a pool of resources protected by fences. Manages resource overflow allocating more
+ * resources.
+ */
+class ResourcePool {
+public:
+ explicit ResourcePool(MasterSemaphore& master_semaphore, size_t grow_step);
+ virtual ~ResourcePool();
+
+protected:
+ size_t CommitResource();
+
+ /// Called when a chunk of resources have to be allocated.
+ virtual void Allocate(size_t begin, size_t end) = 0;
+
+private:
+ /// Manages pool overflow allocating new resources.
+ size_t ManageOverflow();
+
+ /// Allocates a new page of resources.
+ void Grow();
+
+ MasterSemaphore& master_semaphore;
+ size_t grow_step = 0; ///< Number of new resources created after an overflow
+ size_t free_iterator = 0; ///< Hint to where the next free resources is likely to be found
+ std::vector<u64> ticks; ///< Ticks for each resource
+};
+
+} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.cpp b/src/video_core/renderer_vulkan/vk_scheduler.cpp
index dbbd0961a..1a483dc71 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.cpp
+++ b/src/video_core/renderer_vulkan/vk_scheduler.cpp
@@ -10,9 +10,10 @@
#include "common/microprofile.h"
#include "common/thread.h"
+#include "video_core/renderer_vulkan/vk_command_pool.h"
#include "video_core/renderer_vulkan/vk_device.h"
+#include "video_core/renderer_vulkan/vk_master_semaphore.h"
#include "video_core/renderer_vulkan/vk_query_cache.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_state_tracker.h"
#include "video_core/renderer_vulkan/wrapper.h"
@@ -35,10 +36,10 @@ void VKScheduler::CommandChunk::ExecuteAll(vk::CommandBuffer cmdbuf) {
last = nullptr;
}
-VKScheduler::VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
- StateTracker& state_tracker)
- : device{device}, resource_manager{resource_manager}, state_tracker{state_tracker},
- next_fence{&resource_manager.CommitFence()} {
+VKScheduler::VKScheduler(const VKDevice& device_, StateTracker& state_tracker_)
+ : device{device_}, state_tracker{state_tracker_},
+ master_semaphore{std::make_unique<MasterSemaphore>(device)},
+ command_pool{std::make_unique<CommandPool>(*master_semaphore, device)} {
AcquireNewChunk();
AllocateNewContext();
worker_thread = std::thread(&VKScheduler::WorkerThread, this);
@@ -50,20 +51,27 @@ VKScheduler::~VKScheduler() {
worker_thread.join();
}
-void VKScheduler::Flush(bool release_fence, VkSemaphore semaphore) {
+u64 VKScheduler::CurrentTick() const noexcept {
+ return master_semaphore->CurrentTick();
+}
+
+bool VKScheduler::IsFree(u64 tick) const noexcept {
+ return master_semaphore->IsFree(tick);
+}
+
+void VKScheduler::Wait(u64 tick) {
+ master_semaphore->Wait(tick);
+}
+
+void VKScheduler::Flush(VkSemaphore semaphore) {
SubmitExecution(semaphore);
- if (release_fence) {
- current_fence->Release();
- }
AllocateNewContext();
}
-void VKScheduler::Finish(bool release_fence, VkSemaphore semaphore) {
+void VKScheduler::Finish(VkSemaphore semaphore) {
+ const u64 presubmit_tick = CurrentTick();
SubmitExecution(semaphore);
- current_fence->Wait();
- if (release_fence) {
- current_fence->Release();
- }
+ Wait(presubmit_tick);
AllocateNewContext();
}
@@ -160,18 +168,38 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
current_cmdbuf.End();
+ const VkSemaphore timeline_semaphore = master_semaphore->Handle();
+ const u32 num_signal_semaphores = semaphore ? 2U : 1U;
+
+ const u64 signal_value = master_semaphore->CurrentTick();
+ const u64 wait_value = signal_value - 1;
+ const VkPipelineStageFlags wait_stage_mask = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+
+ master_semaphore->NextTick();
+
+ const std::array signal_values{signal_value, u64(0)};
+ const std::array signal_semaphores{timeline_semaphore, semaphore};
+
+ const VkTimelineSemaphoreSubmitInfoKHR timeline_si{
+ .sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO_KHR,
+ .pNext = nullptr,
+ .waitSemaphoreValueCount = 1,
+ .pWaitSemaphoreValues = &wait_value,
+ .signalSemaphoreValueCount = num_signal_semaphores,
+ .pSignalSemaphoreValues = signal_values.data(),
+ };
const VkSubmitInfo submit_info{
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
- .pNext = nullptr,
- .waitSemaphoreCount = 0,
- .pWaitSemaphores = nullptr,
- .pWaitDstStageMask = nullptr,
+ .pNext = &timeline_si,
+ .waitSemaphoreCount = 1,
+ .pWaitSemaphores = &timeline_semaphore,
+ .pWaitDstStageMask = &wait_stage_mask,
.commandBufferCount = 1,
.pCommandBuffers = current_cmdbuf.address(),
- .signalSemaphoreCount = semaphore ? 1U : 0U,
- .pSignalSemaphores = &semaphore,
+ .signalSemaphoreCount = num_signal_semaphores,
+ .pSignalSemaphores = signal_semaphores.data(),
};
- switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info, *current_fence)) {
+ switch (const VkResult result = device.GetGraphicsQueue().Submit(submit_info)) {
case VK_SUCCESS:
break;
case VK_ERROR_DEVICE_LOST:
@@ -183,14 +211,9 @@ void VKScheduler::SubmitExecution(VkSemaphore semaphore) {
}
void VKScheduler::AllocateNewContext() {
- ++ticks;
-
std::unique_lock lock{mutex};
- current_fence = next_fence;
- next_fence = &resource_manager.CommitFence();
- current_cmdbuf = vk::CommandBuffer(resource_manager.CommitCommandBuffer(*current_fence),
- device.GetDispatchLoader());
+ current_cmdbuf = vk::CommandBuffer(command_pool->Commit(), device.GetDispatchLoader());
current_cmdbuf.Begin({
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
.pNext = nullptr,
diff --git a/src/video_core/renderer_vulkan/vk_scheduler.h b/src/video_core/renderer_vulkan/vk_scheduler.h
index 970a65566..7be8a19f0 100644
--- a/src/video_core/renderer_vulkan/vk_scheduler.h
+++ b/src/video_core/renderer_vulkan/vk_scheduler.h
@@ -16,42 +16,33 @@
namespace Vulkan {
+class CommandPool;
+class MasterSemaphore;
class StateTracker;
class VKDevice;
-class VKFence;
class VKQueryCache;
-class VKResourceManager;
-
-class VKFenceView {
-public:
- VKFenceView() = default;
- VKFenceView(VKFence* const& fence) : fence{fence} {}
-
- VKFence* operator->() const noexcept {
- return fence;
- }
-
- operator VKFence&() const noexcept {
- return *fence;
- }
-
-private:
- VKFence* const& fence;
-};
/// The scheduler abstracts command buffer and fence management with an interface that's able to do
/// OpenGL-like operations on Vulkan command buffers.
class VKScheduler {
public:
- explicit VKScheduler(const VKDevice& device, VKResourceManager& resource_manager,
- StateTracker& state_tracker);
+ explicit VKScheduler(const VKDevice& device, StateTracker& state_tracker);
~VKScheduler();
+ /// Returns the current command buffer tick.
+ [[nodiscard]] u64 CurrentTick() const noexcept;
+
+ /// Returns true when a tick has been triggered by the GPU.
+ [[nodiscard]] bool IsFree(u64 tick) const noexcept;
+
+ /// Waits for the given tick to trigger on the GPU.
+ void Wait(u64 tick);
+
/// Sends the current execution context to the GPU.
- void Flush(bool release_fence = true, VkSemaphore semaphore = nullptr);
+ void Flush(VkSemaphore semaphore = nullptr);
/// Sends the current execution context to the GPU and waits for it to complete.
- void Finish(bool release_fence = true, VkSemaphore semaphore = nullptr);
+ void Finish(VkSemaphore semaphore = nullptr);
/// Waits for the worker thread to finish executing everything. After this function returns it's
/// safe to touch worker resources.
@@ -86,14 +77,9 @@ public:
(void)chunk->Record(command);
}
- /// Gets a reference to the current fence.
- VKFenceView GetFence() const {
- return current_fence;
- }
-
- /// Returns the current command buffer tick.
- u64 Ticks() const {
- return ticks;
+ /// Returns the master timeline semaphore.
+ [[nodiscard]] MasterSemaphore& GetMasterSemaphore() const noexcept {
+ return *master_semaphore;
}
private:
@@ -171,6 +157,13 @@ private:
std::array<u8, 0x8000> data{};
};
+ struct State {
+ VkRenderPass renderpass = nullptr;
+ VkFramebuffer framebuffer = nullptr;
+ VkExtent2D render_area = {0, 0};
+ VkPipeline graphics_pipeline = nullptr;
+ };
+
void WorkerThread();
void SubmitExecution(VkSemaphore semaphore);
@@ -186,30 +179,23 @@ private:
void AcquireNewChunk();
const VKDevice& device;
- VKResourceManager& resource_manager;
StateTracker& state_tracker;
+ std::unique_ptr<MasterSemaphore> master_semaphore;
+ std::unique_ptr<CommandPool> command_pool;
+
VKQueryCache* query_cache = nullptr;
vk::CommandBuffer current_cmdbuf;
- VKFence* current_fence = nullptr;
- VKFence* next_fence = nullptr;
-
- struct State {
- VkRenderPass renderpass = nullptr;
- VkFramebuffer framebuffer = nullptr;
- VkExtent2D render_area = {0, 0};
- VkPipeline graphics_pipeline = nullptr;
- } state;
std::unique_ptr<CommandChunk> chunk;
std::thread worker_thread;
+ State state;
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_queue;
Common::SPSCQueue<std::unique_ptr<CommandChunk>> chunk_reserve;
std::mutex mutex;
std::condition_variable cv;
- std::atomic<u64> ticks = 0;
bool quit = false;
};
diff --git a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
index cd7d7a4e4..a20452b87 100644
--- a/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
+++ b/src/video_core/renderer_vulkan/vk_shader_decompiler.cpp
@@ -272,12 +272,19 @@ bool IsPrecise(Operation operand) {
return false;
}
+u32 ShaderVersion(const VKDevice& device) {
+ if (device.InstanceApiVersion() < VK_API_VERSION_1_1) {
+ return 0x00010000;
+ }
+ return 0x00010300;
+}
+
class SPIRVDecompiler final : public Sirit::Module {
public:
explicit SPIRVDecompiler(const VKDevice& device, const ShaderIR& ir, ShaderType stage,
const Registry& registry, const Specialization& specialization)
- : Module(0x00010300), device{device}, ir{ir}, stage{stage}, header{ir.GetHeader()},
- registry{registry}, specialization{specialization} {
+ : Module(ShaderVersion(device)), device{device}, ir{ir}, stage{stage},
+ header{ir.GetHeader()}, registry{registry}, specialization{specialization} {
if (stage != ShaderType::Compute) {
transform_feedback = BuildTransformFeedback(registry.GetGraphicsInfo());
}
@@ -293,6 +300,7 @@ public:
AddCapability(spv::Capability::DrawParameters);
AddCapability(spv::Capability::SubgroupBallotKHR);
AddCapability(spv::Capability::SubgroupVoteKHR);
+ AddExtension("SPV_KHR_16bit_storage");
AddExtension("SPV_KHR_shader_ballot");
AddExtension("SPV_KHR_subgroup_vote");
AddExtension("SPV_KHR_storage_buffer_storage_class");
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
index 5eca0ab91..2fd3b7f39 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.cpp
@@ -10,36 +10,18 @@
#include "common/bit_util.h"
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_device.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_staging_buffer_pool.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
-VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence,
- u64 last_epoch)
- : buffer{std::move(buffer)}, watch{fence}, last_epoch{last_epoch} {}
+VKStagingBufferPool::StagingBuffer::StagingBuffer(std::unique_ptr<VKBuffer> buffer_)
+ : buffer{std::move(buffer_)} {}
-VKStagingBufferPool::StagingBuffer::StagingBuffer(StagingBuffer&& rhs) noexcept {
- buffer = std::move(rhs.buffer);
- watch = std::move(rhs.watch);
- last_epoch = rhs.last_epoch;
-}
-
-VKStagingBufferPool::StagingBuffer::~StagingBuffer() = default;
-
-VKStagingBufferPool::StagingBuffer& VKStagingBufferPool::StagingBuffer::operator=(
- StagingBuffer&& rhs) noexcept {
- buffer = std::move(rhs.buffer);
- watch = std::move(rhs.watch);
- last_epoch = rhs.last_epoch;
- return *this;
-}
-
-VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device, VKMemoryManager& memory_manager,
- VKScheduler& scheduler)
- : device{device}, memory_manager{memory_manager}, scheduler{scheduler} {}
+VKStagingBufferPool::VKStagingBufferPool(const VKDevice& device_, VKMemoryManager& memory_manager_,
+ VKScheduler& scheduler_)
+ : device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_} {}
VKStagingBufferPool::~VKStagingBufferPool() = default;
@@ -51,7 +33,6 @@ VKBuffer& VKStagingBufferPool::GetUnusedBuffer(std::size_t size, bool host_visib
}
void VKStagingBufferPool::TickFrame() {
- ++epoch;
current_delete_level = (current_delete_level + 1) % NumLevels;
ReleaseCache(true);
@@ -59,11 +40,12 @@ void VKStagingBufferPool::TickFrame() {
}
VKBuffer* VKStagingBufferPool::TryGetReservedBuffer(std::size_t size, bool host_visible) {
- for (auto& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
- if (entry.watch.TryWatch(scheduler.GetFence())) {
- entry.last_epoch = epoch;
- return &*entry.buffer;
+ for (StagingBuffer& entry : GetCache(host_visible)[Common::Log2Ceil64(size)].entries) {
+ if (!scheduler.IsFree(entry.tick)) {
+ continue;
}
+ entry.tick = scheduler.CurrentTick();
+ return &*entry.buffer;
}
return nullptr;
}
@@ -86,8 +68,10 @@ VKBuffer& VKStagingBufferPool::CreateStagingBuffer(std::size_t size, bool host_v
});
buffer->commit = memory_manager.Commit(buffer->handle, host_visible);
- auto& entries = GetCache(host_visible)[log2].entries;
- return *entries.emplace_back(std::move(buffer), scheduler.GetFence(), epoch).buffer;
+ std::vector<StagingBuffer>& entries = GetCache(host_visible)[log2].entries;
+ StagingBuffer& entry = entries.emplace_back(std::move(buffer));
+ entry.tick = scheduler.CurrentTick();
+ return *entry.buffer;
}
VKStagingBufferPool::StagingBuffersCache& VKStagingBufferPool::GetCache(bool host_visible) {
@@ -109,9 +93,8 @@ u64 VKStagingBufferPool::ReleaseLevel(StagingBuffersCache& cache, std::size_t lo
auto& entries = staging.entries;
const std::size_t old_size = entries.size();
- const auto is_deleteable = [this](const auto& entry) {
- static constexpr u64 epochs_to_destroy = 180;
- return entry.last_epoch + epochs_to_destroy < epoch && !entry.watch.IsUsed();
+ const auto is_deleteable = [this](const StagingBuffer& entry) {
+ return scheduler.IsFree(entry.tick);
};
const std::size_t begin_offset = staging.delete_index;
const std::size_t end_offset = std::min(begin_offset + deletions_per_tick, old_size);
diff --git a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
index 3c4901437..2dd5049ac 100644
--- a/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
+++ b/src/video_core/renderer_vulkan/vk_staging_buffer_pool.h
@@ -10,13 +10,11 @@
#include "common/common_types.h"
#include "video_core/renderer_vulkan/vk_memory_manager.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/wrapper.h"
namespace Vulkan {
class VKDevice;
-class VKFenceWatch;
class VKScheduler;
struct VKBuffer final {
@@ -36,16 +34,10 @@ public:
private:
struct StagingBuffer final {
- explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer, VKFence& fence, u64 last_epoch);
- StagingBuffer(StagingBuffer&& rhs) noexcept;
- StagingBuffer(const StagingBuffer&) = delete;
- ~StagingBuffer();
-
- StagingBuffer& operator=(StagingBuffer&& rhs) noexcept;
+ explicit StagingBuffer(std::unique_ptr<VKBuffer> buffer);
std::unique_ptr<VKBuffer> buffer;
- VKFenceWatch watch;
- u64 last_epoch = 0;
+ u64 tick = 0;
};
struct StagingBuffers final {
@@ -73,8 +65,6 @@ private:
StagingBuffersCache host_staging_buffers;
StagingBuffersCache device_staging_buffers;
- u64 epoch = 0;
-
std::size_t current_delete_level = 0;
};
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.cpp b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
index 9151d9fb1..5d2c4a796 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.cpp
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.cpp
@@ -42,7 +42,6 @@ Flags MakeInvalidationFlags() {
flags[DepthWriteEnable] = true;
flags[DepthCompareOp] = true;
flags[FrontFace] = true;
- flags[PrimitiveTopology] = true;
flags[StencilOp] = true;
flags[StencilTestEnable] = true;
return flags;
@@ -112,10 +111,6 @@ void SetupDirtyFrontFace(Tables& tables) {
table[OFF(screen_y_control)] = FrontFace;
}
-void SetupDirtyPrimitiveTopology(Tables& tables) {
- tables[0][OFF(draw.topology)] = PrimitiveTopology;
-}
-
void SetupDirtyStencilOp(Tables& tables) {
auto& table = tables[0];
table[OFF(stencil_front_op_fail)] = StencilOp;
@@ -137,12 +132,9 @@ void SetupDirtyStencilTestEnable(Tables& tables) {
} // Anonymous namespace
-StateTracker::StateTracker(Core::System& system)
- : system{system}, invalidation_flags{MakeInvalidationFlags()} {}
-
-void StateTracker::Initialize() {
- auto& dirty = system.GPU().Maxwell3D().dirty;
- auto& tables = dirty.tables;
+StateTracker::StateTracker(Tegra::GPU& gpu)
+ : flags{gpu.Maxwell3D().dirty.flags}, invalidation_flags{MakeInvalidationFlags()} {
+ auto& tables = gpu.Maxwell3D().dirty.tables;
SetupDirtyRenderTargets(tables);
SetupDirtyViewports(tables);
SetupDirtyScissors(tables);
@@ -156,13 +148,8 @@ void StateTracker::Initialize() {
SetupDirtyDepthWriteEnable(tables);
SetupDirtyDepthCompareOp(tables);
SetupDirtyFrontFace(tables);
- SetupDirtyPrimitiveTopology(tables);
SetupDirtyStencilOp(tables);
SetupDirtyStencilTestEnable(tables);
}
-void StateTracker::InvalidateCommandBufferState() {
- system.GPU().Maxwell3D().dirty.flags |= invalidation_flags;
-}
-
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_state_tracker.h b/src/video_core/renderer_vulkan/vk_state_tracker.h
index 54ca0d6c6..1de789e57 100644
--- a/src/video_core/renderer_vulkan/vk_state_tracker.h
+++ b/src/video_core/renderer_vulkan/vk_state_tracker.h
@@ -32,7 +32,6 @@ enum : u8 {
DepthWriteEnable,
DepthCompareOp,
FrontFace,
- PrimitiveTopology,
StencilOp,
StencilTestEnable,
@@ -43,12 +42,15 @@ static_assert(Last <= std::numeric_limits<u8>::max());
} // namespace Dirty
class StateTracker {
-public:
- explicit StateTracker(Core::System& system);
+ using Maxwell = Tegra::Engines::Maxwell3D::Regs;
- void Initialize();
+public:
+ explicit StateTracker(Tegra::GPU& gpu);
- void InvalidateCommandBufferState();
+ void InvalidateCommandBufferState() {
+ flags |= invalidation_flags;
+ current_topology = INVALID_TOPOLOGY;
+ }
bool TouchViewports() {
return Exchange(Dirty::Viewports, false);
@@ -102,10 +104,6 @@ public:
return Exchange(Dirty::FrontFace, false);
}
- bool TouchPrimitiveTopology() {
- return Exchange(Dirty::PrimitiveTopology, false);
- }
-
bool TouchStencilOp() {
return Exchange(Dirty::StencilOp, false);
}
@@ -114,16 +112,24 @@ public:
return Exchange(Dirty::StencilTestEnable, false);
}
+ bool ChangePrimitiveTopology(Maxwell::PrimitiveTopology new_topology) {
+ const bool has_changed = current_topology != new_topology;
+ current_topology = new_topology;
+ return has_changed;
+ }
+
private:
+ static constexpr auto INVALID_TOPOLOGY = static_cast<Maxwell::PrimitiveTopology>(~0u);
+
bool Exchange(std::size_t id, bool new_value) const noexcept {
- auto& flags = system.GPU().Maxwell3D().dirty.flags;
const bool is_dirty = flags[id];
flags[id] = new_value;
return is_dirty;
}
- Core::System& system;
+ Tegra::Engines::Maxwell3D::DirtyState::Flags& flags;
Tegra::Engines::Maxwell3D::DirtyState::Flags invalidation_flags;
+ Maxwell::PrimitiveTopology current_topology = INVALID_TOPOLOGY;
};
} // namespace Vulkan
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
index a5526a3f5..1b59612b9 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.cpp
@@ -11,7 +11,6 @@
#include "common/alignment.h"
#include "common/assert.h"
#include "video_core/renderer_vulkan/vk_device.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_stream_buffer.h"
#include "video_core/renderer_vulkan/wrapper.h"
@@ -57,9 +56,9 @@ u32 GetMemoryType(const VkPhysicalDeviceMemoryProperties& properties,
} // Anonymous namespace
-VKStreamBuffer::VKStreamBuffer(const VKDevice& device, VKScheduler& scheduler,
+VKStreamBuffer::VKStreamBuffer(const VKDevice& device_, VKScheduler& scheduler_,
VkBufferUsageFlags usage)
- : device{device}, scheduler{scheduler} {
+ : device{device_}, scheduler{scheduler_} {
CreateBuffers(usage);
ReserveWatches(current_watches, WATCHES_INITIAL_RESERVE);
ReserveWatches(previous_watches, WATCHES_INITIAL_RESERVE);
@@ -111,7 +110,7 @@ void VKStreamBuffer::Unmap(u64 size) {
}
auto& watch = current_watches[current_watch_cursor++];
watch.upper_bound = offset;
- watch.fence.Watch(scheduler.GetFence());
+ watch.tick = scheduler.CurrentTick();
}
void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
@@ -121,7 +120,8 @@ void VKStreamBuffer::CreateBuffers(VkBufferUsageFlags usage) {
// Substract from the preferred heap size some bytes to avoid getting out of memory.
const VkDeviceSize heap_size = memory_properties.memoryHeaps[preferred_heap].size;
- const VkDeviceSize allocable_size = heap_size - 9 * 1024 * 1024;
+ // As per DXVK's example, using `heap_size / 2`
+ const VkDeviceSize allocable_size = heap_size / 2;
buffer = device.GetLogical().CreateBuffer({
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.pNext = nullptr,
@@ -157,7 +157,7 @@ void VKStreamBuffer::WaitPendingOperations(u64 requested_upper_bound) {
while (requested_upper_bound < wait_bound && wait_cursor < *invalidation_mark) {
auto& watch = previous_watches[wait_cursor];
wait_bound = watch.upper_bound;
- watch.fence.Wait();
+ scheduler.Wait(watch.tick);
++wait_cursor;
}
}
diff --git a/src/video_core/renderer_vulkan/vk_stream_buffer.h b/src/video_core/renderer_vulkan/vk_stream_buffer.h
index 689f0d276..5e15ad78f 100644
--- a/src/video_core/renderer_vulkan/vk_stream_buffer.h
+++ b/src/video_core/renderer_vulkan/vk_stream_buffer.h
@@ -14,7 +14,6 @@
namespace Vulkan {
class VKDevice;
-class VKFence;
class VKFenceWatch;
class VKScheduler;
@@ -44,8 +43,8 @@ public:
}
private:
- struct Watch final {
- VKFenceWatch fence;
+ struct Watch {
+ u64 tick{};
u64 upper_bound{};
};
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.cpp b/src/video_core/renderer_vulkan/vk_swapchain.cpp
index 6bfd2abae..9636a7c65 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.cpp
+++ b/src/video_core/renderer_vulkan/vk_swapchain.cpp
@@ -12,7 +12,7 @@
#include "core/core.h"
#include "core/frontend/framebuffer_layout.h"
#include "video_core/renderer_vulkan/vk_device.h"
-#include "video_core/renderer_vulkan/vk_resource_manager.h"
+#include "video_core/renderer_vulkan/vk_scheduler.h"
#include "video_core/renderer_vulkan/vk_swapchain.h"
#include "video_core/renderer_vulkan/wrapper.h"
@@ -56,8 +56,8 @@ VkExtent2D ChooseSwapExtent(const VkSurfaceCapabilitiesKHR& capabilities, u32 wi
} // Anonymous namespace
-VKSwapchain::VKSwapchain(VkSurfaceKHR surface, const VKDevice& device)
- : surface{surface}, device{device} {}
+VKSwapchain::VKSwapchain(VkSurfaceKHR surface_, const VKDevice& device_, VKScheduler& scheduler_)
+ : surface{surface_}, device{device_}, scheduler{scheduler_} {}
VKSwapchain::~VKSwapchain() = default;
@@ -75,21 +75,18 @@ void VKSwapchain::Create(u32 width, u32 height, bool srgb) {
CreateSemaphores();
CreateImageViews();
- fences.resize(image_count, nullptr);
+ resource_ticks.clear();
+ resource_ticks.resize(image_count);
}
void VKSwapchain::AcquireNextImage() {
device.GetLogical().AcquireNextImageKHR(*swapchain, std::numeric_limits<u64>::max(),
*present_semaphores[frame_index], {}, &image_index);
- if (auto& fence = fences[image_index]; fence) {
- fence->Wait();
- fence->Release();
- fence = nullptr;
- }
+ scheduler.Wait(resource_ticks[image_index]);
}
-bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) {
+bool VKSwapchain::Present(VkSemaphore render_semaphore) {
const VkSemaphore present_semaphore{*present_semaphores[frame_index]};
const std::array<VkSemaphore, 2> semaphores{present_semaphore, render_semaphore};
const auto present_queue{device.GetPresentQueue()};
@@ -123,8 +120,7 @@ bool VKSwapchain::Present(VkSemaphore render_semaphore, VKFence& fence) {
break;
}
- ASSERT(fences[image_index] == nullptr);
- fences[image_index] = &fence;
+ resource_ticks[image_index] = scheduler.CurrentTick();
frame_index = (frame_index + 1) % static_cast<u32>(image_count);
return recreated;
}
diff --git a/src/video_core/renderer_vulkan/vk_swapchain.h b/src/video_core/renderer_vulkan/vk_swapchain.h
index a35d61345..6b39befdf 100644
--- a/src/video_core/renderer_vulkan/vk_swapchain.h
+++ b/src/video_core/renderer_vulkan/vk_swapchain.h
@@ -16,11 +16,11 @@ struct FramebufferLayout;
namespace Vulkan {
class VKDevice;
-class VKFence;
+class VKScheduler;
class VKSwapchain {
public:
- explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device);
+ explicit VKSwapchain(VkSurfaceKHR surface, const VKDevice& device, VKScheduler& scheduler);
~VKSwapchain();
/// Creates (or recreates) the swapchain with a given size.
@@ -31,7 +31,7 @@ public:
/// Presents the rendered image to the swapchain. Returns true when the swapchains had to be
/// recreated. Takes responsability for the ownership of fence.
- bool Present(VkSemaphore render_semaphore, VKFence& fence);
+ bool Present(VkSemaphore render_semaphore);
/// Returns true when the framebuffer layout has changed.
bool HasFramebufferChanged(const Layout::FramebufferLayout& framebuffer) const;
@@ -74,6 +74,7 @@ private:
const VkSurfaceKHR surface;
const VKDevice& device;
+ VKScheduler& scheduler;
vk::SwapchainKHR swapchain;
@@ -81,7 +82,7 @@ private:
std::vector<VkImage> images;
std::vector<vk::ImageView> image_views;
std::vector<vk::Framebuffer> framebuffers;
- std::vector<VKFence*> fences;
+ std::vector<u64> resource_ticks;
std::vector<vk::Semaphore> present_semaphores;
u32 image_index{};
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.cpp b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
index 2c6f54101..f2c8f2ae1 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.cpp
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.cpp
@@ -188,12 +188,10 @@ u32 EncodeSwizzle(Tegra::Texture::SwizzleSource x_source, Tegra::Texture::Swizzl
} // Anonymous namespace
-CachedSurface::CachedSurface(Core::System& system, const VKDevice& device,
- VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
+CachedSurface::CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager,
VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
GPUVAddr gpu_addr, const SurfaceParams& params)
- : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, system{system},
- device{device}, resource_manager{resource_manager},
+ : SurfaceBase<View>{gpu_addr, params, device.IsOptimalAstcSupported()}, device{device},
memory_manager{memory_manager}, scheduler{scheduler}, staging_pool{staging_pool} {
if (params.IsBuffer()) {
buffer = CreateBuffer(device, params, host_memory_size);
@@ -490,19 +488,20 @@ VkImageView CachedSurfaceView::GetAttachment() {
return *render_target;
}
-VKTextureCache::VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- const VKDevice& device, VKResourceManager& resource_manager,
- VKMemoryManager& memory_manager, VKScheduler& scheduler,
- VKStagingBufferPool& staging_pool)
- : TextureCache(system, rasterizer, device.IsOptimalAstcSupported()), device{device},
- resource_manager{resource_manager}, memory_manager{memory_manager}, scheduler{scheduler},
- staging_pool{staging_pool} {}
+VKTextureCache::VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::Engines::Maxwell3D& maxwell3d,
+ Tegra::MemoryManager& gpu_memory, const VKDevice& device_,
+ VKMemoryManager& memory_manager_, VKScheduler& scheduler_,
+ VKStagingBufferPool& staging_pool_)
+ : TextureCache(rasterizer, maxwell3d, gpu_memory, device_.IsOptimalAstcSupported()),
+ device{device_}, memory_manager{memory_manager_}, scheduler{scheduler_}, staging_pool{
+ staging_pool_} {}
VKTextureCache::~VKTextureCache() = default;
Surface VKTextureCache::CreateSurface(GPUVAddr gpu_addr, const SurfaceParams& params) {
- return std::make_shared<CachedSurface>(system, device, resource_manager, memory_manager,
- scheduler, staging_pool, gpu_addr, params);
+ return std::make_shared<CachedSurface>(device, memory_manager, scheduler, staging_pool,
+ gpu_addr, params);
}
void VKTextureCache::ImageCopy(Surface& src_surface, Surface& dst_surface,
diff --git a/src/video_core/renderer_vulkan/vk_texture_cache.h b/src/video_core/renderer_vulkan/vk_texture_cache.h
index 807e26c8a..39202feba 100644
--- a/src/video_core/renderer_vulkan/vk_texture_cache.h
+++ b/src/video_core/renderer_vulkan/vk_texture_cache.h
@@ -15,10 +15,6 @@
#include "video_core/texture_cache/surface_base.h"
#include "video_core/texture_cache/texture_cache.h"
-namespace Core {
-class System;
-}
-
namespace VideoCore {
class RasterizerInterface;
}
@@ -27,7 +23,6 @@ namespace Vulkan {
class RasterizerVulkan;
class VKDevice;
-class VKResourceManager;
class VKScheduler;
class VKStagingBufferPool;
@@ -45,8 +40,7 @@ class CachedSurface final : public VideoCommon::SurfaceBase<View> {
friend CachedSurfaceView;
public:
- explicit CachedSurface(Core::System& system, const VKDevice& device,
- VKResourceManager& resource_manager, VKMemoryManager& memory_manager,
+ explicit CachedSurface(const VKDevice& device, VKMemoryManager& memory_manager,
VKScheduler& scheduler, VKStagingBufferPool& staging_pool,
GPUVAddr gpu_addr, const SurfaceParams& params);
~CachedSurface();
@@ -101,9 +95,7 @@ private:
VkImageSubresourceRange GetImageSubresourceRange() const;
- Core::System& system;
const VKDevice& device;
- VKResourceManager& resource_manager;
VKMemoryManager& memory_manager;
VKScheduler& scheduler;
VKStagingBufferPool& staging_pool;
@@ -201,10 +193,10 @@ private:
class VKTextureCache final : public TextureCacheBase {
public:
- explicit VKTextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- const VKDevice& device, VKResourceManager& resource_manager,
- VKMemoryManager& memory_manager, VKScheduler& scheduler,
- VKStagingBufferPool& staging_pool);
+ explicit VKTextureCache(VideoCore::RasterizerInterface& rasterizer,
+ Tegra::Engines::Maxwell3D& maxwell3d, Tegra::MemoryManager& gpu_memory,
+ const VKDevice& device, VKMemoryManager& memory_manager,
+ VKScheduler& scheduler, VKStagingBufferPool& staging_pool);
~VKTextureCache();
private:
@@ -219,7 +211,6 @@ private:
void BufferCopy(Surface& src_surface, Surface& dst_surface) override;
const VKDevice& device;
- VKResourceManager& resource_manager;
VKMemoryManager& memory_manager;
VKScheduler& scheduler;
VKStagingBufferPool& staging_pool;
diff --git a/src/video_core/renderer_vulkan/wrapper.cpp b/src/video_core/renderer_vulkan/wrapper.cpp
index 013865aa4..4e83303d8 100644
--- a/src/video_core/renderer_vulkan/wrapper.cpp
+++ b/src/video_core/renderer_vulkan/wrapper.cpp
@@ -6,10 +6,12 @@
#include <exception>
#include <memory>
#include <optional>
+#include <string_view>
#include <utility>
#include <vector>
#include "common/common_types.h"
+#include "common/logging/log.h"
#include "video_core/renderer_vulkan/wrapper.h"
@@ -17,21 +19,42 @@ namespace Vulkan::vk {
namespace {
+template <typename Func>
+void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld,
+ Func&& func) {
+ // Calling GetProperties calls Vulkan more than needed. But they are supposed to be cheap
+ // functions.
+ std::stable_sort(devices.begin(), devices.end(),
+ [&dld, &func](VkPhysicalDevice lhs, VkPhysicalDevice rhs) {
+ return func(vk::PhysicalDevice(lhs, dld).GetProperties(),
+ vk::PhysicalDevice(rhs, dld).GetProperties());
+ });
+}
+
+void SortPhysicalDevicesPerVendor(std::vector<VkPhysicalDevice>& devices,
+ const InstanceDispatch& dld,
+ std::initializer_list<u32> vendor_ids) {
+ for (auto it = vendor_ids.end(); it != vendor_ids.begin();) {
+ --it;
+ SortPhysicalDevices(devices, dld, [id = *it](const auto& lhs, const auto& rhs) {
+ return lhs.vendorID == id && rhs.vendorID != id;
+ });
+ }
+}
+
void SortPhysicalDevices(std::vector<VkPhysicalDevice>& devices, const InstanceDispatch& dld) {
- std::stable_sort(devices.begin(), devices.end(), [&](auto lhs, auto rhs) {
- // This will call Vulkan more than needed, but these calls are cheap.
- const auto lhs_properties = vk::PhysicalDevice(lhs, dld).GetProperties();
- const auto rhs_properties = vk::PhysicalDevice(rhs, dld).GetProperties();
-
- // Prefer discrete GPUs, Nvidia over AMD, AMD over Intel, Intel over the rest.
- const bool preferred =
- (lhs_properties.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
- rhs_properties.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) ||
- (lhs_properties.vendorID == 0x10DE && rhs_properties.vendorID != 0x10DE) ||
- (lhs_properties.vendorID == 0x1002 && rhs_properties.vendorID != 0x1002) ||
- (lhs_properties.vendorID == 0x8086 && rhs_properties.vendorID != 0x8086);
- return !preferred;
+ // Sort by name, this will set a base and make GPUs with higher numbers appear first
+ // (e.g. GTX 1650 will intentionally be listed before a GTX 1080).
+ SortPhysicalDevices(devices, dld, [](const auto& lhs, const auto& rhs) {
+ return std::string_view{lhs.deviceName} > std::string_view{rhs.deviceName};
});
+ // Prefer discrete over non-discrete
+ SortPhysicalDevices(devices, dld, [](const auto& lhs, const auto& rhs) {
+ return lhs.deviceType == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU &&
+ rhs.deviceType != VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU;
+ });
+ // Prefer Nvidia over AMD, AMD over Intel, Intel over the rest.
+ SortPhysicalDevicesPerVendor(devices, dld, {0x10DE, 0x1002, 0x8086});
}
template <typename T>
@@ -148,6 +171,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkGetFenceStatus);
X(vkGetImageMemoryRequirements);
X(vkGetQueryPoolResults);
+ X(vkGetSemaphoreCounterValueKHR);
X(vkMapMemory);
X(vkQueueSubmit);
X(vkResetFences);
@@ -156,6 +180,7 @@ void Load(VkDevice device, DeviceDispatch& dld) noexcept {
X(vkUpdateDescriptorSetWithTemplateKHR);
X(vkUpdateDescriptorSets);
X(vkWaitForFences);
+ X(vkWaitSemaphoresKHR);
#undef X
}
@@ -262,6 +287,22 @@ const char* ToString(VkResult result) noexcept {
return "VK_ERROR_INVALID_DEVICE_ADDRESS_EXT";
case VkResult::VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT:
return "VK_ERROR_FULL_SCREEN_EXCLUSIVE_MODE_LOST_EXT";
+ case VkResult::VK_ERROR_UNKNOWN:
+ return "VK_ERROR_UNKNOWN";
+ case VkResult::VK_ERROR_INCOMPATIBLE_VERSION_KHR:
+ return "VK_ERROR_INCOMPATIBLE_VERSION_KHR";
+ case VkResult::VK_THREAD_IDLE_KHR:
+ return "VK_THREAD_IDLE_KHR";
+ case VkResult::VK_THREAD_DONE_KHR:
+ return "VK_THREAD_DONE_KHR";
+ case VkResult::VK_OPERATION_DEFERRED_KHR:
+ return "VK_OPERATION_DEFERRED_KHR";
+ case VkResult::VK_OPERATION_NOT_DEFERRED_KHR:
+ return "VK_OPERATION_NOT_DEFERRED_KHR";
+ case VkResult::VK_PIPELINE_COMPILE_REQUIRED_EXT:
+ return "VK_PIPELINE_COMPILE_REQUIRED_EXT";
+ case VkResult::VK_RESULT_MAX_ENUM:
+ return "VK_RESULT_MAX_ENUM";
}
return "Unknown";
}
@@ -375,18 +416,17 @@ VkResult Free(VkDevice device, VkCommandPool handle, Span<VkCommandBuffer> buffe
return VK_SUCCESS;
}
-Instance Instance::Create(Span<const char*> layers, Span<const char*> extensions,
+Instance Instance::Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dld) noexcept {
- static constexpr VkApplicationInfo application_info{
+ const VkApplicationInfo application_info{
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pNext = nullptr,
.pApplicationName = "yuzu Emulator",
.applicationVersion = VK_MAKE_VERSION(0, 1, 0),
.pEngineName = "yuzu Emulator",
.engineVersion = VK_MAKE_VERSION(0, 1, 0),
- .apiVersion = VK_API_VERSION_1_1,
+ .apiVersion = version,
};
-
const VkInstanceCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pNext = nullptr,
@@ -558,7 +598,10 @@ Semaphore Device::CreateSemaphore() const {
.pNext = nullptr,
.flags = 0,
};
+ return CreateSemaphore(ci);
+}
+Semaphore Device::CreateSemaphore(const VkSemaphoreCreateInfo& ci) const {
VkSemaphore object;
Check(dld->vkCreateSemaphore(handle, &ci, nullptr, &object));
return Semaphore(object, handle, *dld);
@@ -644,7 +687,7 @@ ShaderModule Device::CreateShaderModule(const VkShaderModuleCreateInfo& ci) cons
return ShaderModule(object, handle, *dld);
}
-Event Device::CreateNewEvent() const {
+Event Device::CreateEvent() const {
static constexpr VkEventCreateInfo ci{
.sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
.pNext = nullptr,
@@ -775,6 +818,21 @@ VkPhysicalDeviceMemoryProperties PhysicalDevice::GetMemoryProperties() const noe
return properties;
}
+u32 AvailableVersion(const InstanceDispatch& dld) noexcept {
+ PFN_vkEnumerateInstanceVersion vkEnumerateInstanceVersion;
+ if (!Proc(vkEnumerateInstanceVersion, dld, "vkEnumerateInstanceVersion")) {
+ // If the procedure is not found, Vulkan 1.0 is assumed
+ return VK_API_VERSION_1_0;
+ }
+ u32 version;
+ if (const VkResult result = vkEnumerateInstanceVersion(&version); result != VK_SUCCESS) {
+ LOG_ERROR(Render_Vulkan, "vkEnumerateInstanceVersion returned {}, assuming Vulkan 1.1",
+ ToString(result));
+ return VK_API_VERSION_1_1;
+ }
+ return version;
+}
+
std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties(
const InstanceDispatch& dld) {
u32 num;
@@ -786,7 +844,7 @@ std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProp
VK_SUCCESS) {
return std::nullopt;
}
- return std::move(properties);
+ return properties;
}
std::optional<std::vector<VkLayerProperties>> EnumerateInstanceLayerProperties(
diff --git a/src/video_core/renderer_vulkan/wrapper.h b/src/video_core/renderer_vulkan/wrapper.h
index b9d3fedc1..f64919623 100644
--- a/src/video_core/renderer_vulkan/wrapper.h
+++ b/src/video_core/renderer_vulkan/wrapper.h
@@ -267,6 +267,7 @@ struct DeviceDispatch : public InstanceDispatch {
PFN_vkGetFenceStatus vkGetFenceStatus;
PFN_vkGetImageMemoryRequirements vkGetImageMemoryRequirements;
PFN_vkGetQueryPoolResults vkGetQueryPoolResults;
+ PFN_vkGetSemaphoreCounterValueKHR vkGetSemaphoreCounterValueKHR;
PFN_vkMapMemory vkMapMemory;
PFN_vkQueueSubmit vkQueueSubmit;
PFN_vkResetFences vkResetFences;
@@ -275,6 +276,7 @@ struct DeviceDispatch : public InstanceDispatch {
PFN_vkUpdateDescriptorSetWithTemplateKHR vkUpdateDescriptorSetWithTemplateKHR;
PFN_vkUpdateDescriptorSets vkUpdateDescriptorSets;
PFN_vkWaitForFences vkWaitForFences;
+ PFN_vkWaitSemaphoresKHR vkWaitSemaphoresKHR;
};
/// Loads instance agnostic function pointers.
@@ -550,7 +552,6 @@ using PipelineLayout = Handle<VkPipelineLayout, VkDevice, DeviceDispatch>;
using QueryPool = Handle<VkQueryPool, VkDevice, DeviceDispatch>;
using RenderPass = Handle<VkRenderPass, VkDevice, DeviceDispatch>;
using Sampler = Handle<VkSampler, VkDevice, DeviceDispatch>;
-using Semaphore = Handle<VkSemaphore, VkDevice, DeviceDispatch>;
using ShaderModule = Handle<VkShaderModule, VkDevice, DeviceDispatch>;
using SurfaceKHR = Handle<VkSurfaceKHR, VkInstance, InstanceDispatch>;
@@ -563,7 +564,7 @@ class Instance : public Handle<VkInstance, NoOwner, InstanceDispatch> {
public:
/// Creates a Vulkan instance. Use "operator bool" for error handling.
- static Instance Create(Span<const char*> layers, Span<const char*> extensions,
+ static Instance Create(u32 version, Span<const char*> layers, Span<const char*> extensions,
InstanceDispatch& dld) noexcept;
/// Enumerates physical devices.
@@ -582,7 +583,8 @@ public:
/// Construct a queue handle.
constexpr Queue(VkQueue queue, const DeviceDispatch& dld) noexcept : queue{queue}, dld{&dld} {}
- VkResult Submit(Span<VkSubmitInfo> submit_infos, VkFence fence) const noexcept {
+ VkResult Submit(Span<VkSubmitInfo> submit_infos,
+ VkFence fence = VK_NULL_HANDLE) const noexcept {
return dld->vkQueueSubmit(queue, submit_infos.size(), submit_infos.data(), fence);
}
@@ -674,6 +676,44 @@ public:
}
};
+class Semaphore : public Handle<VkSemaphore, VkDevice, DeviceDispatch> {
+ using Handle<VkSemaphore, VkDevice, DeviceDispatch>::Handle;
+
+public:
+ [[nodiscard]] u64 GetCounter() const {
+ u64 value;
+ Check(dld->vkGetSemaphoreCounterValueKHR(owner, handle, &value));
+ return value;
+ }
+
+ /**
+ * Waits for a timeline semaphore on the host.
+ *
+ * @param value Value to wait
+ * @param timeout Time in nanoseconds to timeout
+ * @return True on successful wait, false on timeout
+ */
+ bool Wait(u64 value, u64 timeout = std::numeric_limits<u64>::max()) const {
+ const VkSemaphoreWaitInfoKHR wait_info{
+ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO_KHR,
+ .pNext = nullptr,
+ .flags = 0,
+ .semaphoreCount = 1,
+ .pSemaphores = &handle,
+ .pValues = &value,
+ };
+ const VkResult result = dld->vkWaitSemaphoresKHR(owner, &wait_info, timeout);
+ switch (result) {
+ case VK_SUCCESS:
+ return true;
+ case VK_TIMEOUT:
+ return false;
+ default:
+ throw Exception(result);
+ }
+ }
+};
+
class Device : public Handle<VkDevice, NoOwner, DeviceDispatch> {
using Handle<VkDevice, NoOwner, DeviceDispatch>::Handle;
@@ -694,6 +734,8 @@ public:
Semaphore CreateSemaphore() const;
+ Semaphore CreateSemaphore(const VkSemaphoreCreateInfo& ci) const;
+
Fence CreateFence(const VkFenceCreateInfo& ci) const;
DescriptorPool CreateDescriptorPool(const VkDescriptorPoolCreateInfo& ci) const;
@@ -721,7 +763,7 @@ public:
ShaderModule CreateShaderModule(const VkShaderModuleCreateInfo& ci) const;
- Event CreateNewEvent() const;
+ Event CreateEvent() const;
SwapchainKHR CreateSwapchainKHR(const VkSwapchainCreateInfoKHR& ci) const;
@@ -1048,6 +1090,8 @@ private:
const DeviceDispatch* dld;
};
+u32 AvailableVersion(const InstanceDispatch& dld) noexcept;
+
std::optional<std::vector<VkExtensionProperties>> EnumerateInstanceExtensionProperties(
const InstanceDispatch& dld);
diff --git a/src/video_core/shader/ast.h b/src/video_core/shader/ast.h
index cca13bcde..8e5a22ab3 100644
--- a/src/video_core/shader/ast.h
+++ b/src/video_core/shader/ast.h
@@ -199,55 +199,48 @@ public:
}
std::optional<u32> GetGotoLabel() const {
- auto inner = std::get_if<ASTGoto>(&data);
- if (inner) {
+ if (const auto* inner = std::get_if<ASTGoto>(&data)) {
return {inner->label};
}
- return {};
+ return std::nullopt;
}
Expr GetGotoCondition() const {
- auto inner = std::get_if<ASTGoto>(&data);
- if (inner) {
+ if (const auto* inner = std::get_if<ASTGoto>(&data)) {
return inner->condition;
}
return nullptr;
}
void MarkLabelUnused() {
- auto inner = std::get_if<ASTLabel>(&data);
- if (inner) {
+ if (auto* inner = std::get_if<ASTLabel>(&data)) {
inner->unused = true;
}
}
bool IsLabelUnused() const {
- auto inner = std::get_if<ASTLabel>(&data);
- if (inner) {
+ if (const auto* inner = std::get_if<ASTLabel>(&data)) {
return inner->unused;
}
return true;
}
std::optional<u32> GetLabelIndex() const {
- auto inner = std::get_if<ASTLabel>(&data);
- if (inner) {
+ if (const auto* inner = std::get_if<ASTLabel>(&data)) {
return {inner->index};
}
- return {};
+ return std::nullopt;
}
Expr GetIfCondition() const {
- auto inner = std::get_if<ASTIfThen>(&data);
- if (inner) {
+ if (const auto* inner = std::get_if<ASTIfThen>(&data)) {
return inner->condition;
}
return nullptr;
}
void SetGotoCondition(Expr new_condition) {
- auto inner = std::get_if<ASTGoto>(&data);
- if (inner) {
+ if (auto* inner = std::get_if<ASTGoto>(&data)) {
inner->condition = std::move(new_condition);
}
}
diff --git a/src/video_core/shader/async_shaders.cpp b/src/video_core/shader/async_shaders.cpp
index f815584f7..6920afdf2 100644
--- a/src/video_core/shader/async_shaders.cpp
+++ b/src/video_core/shader/async_shaders.cpp
@@ -20,14 +20,15 @@ AsyncShaders::~AsyncShaders() {
}
void AsyncShaders::AllocateWorkers() {
- // Max worker threads we should allow
- constexpr u32 MAX_THREADS = 4;
- // Deduce how many threads we can use
- const u32 threads_used = std::thread::hardware_concurrency() / 4;
- // Always allow at least 1 thread regardless of our settings
- const auto max_worker_count = std::max(1U, threads_used);
- // Don't use more than MAX_THREADS
- const auto num_workers = std::min(max_worker_count, MAX_THREADS);
+ // Use at least one thread
+ u32 num_workers = 1;
+
+ // Deduce how many more threads we can use
+ const u32 thread_count = std::thread::hardware_concurrency();
+ if (thread_count >= 8) {
+ // Increase async workers by 1 for every 2 threads >= 8
+ num_workers += 1 + (thread_count - 8) / 2;
+ }
// If we already have workers queued, ignore
if (num_workers == worker_threads.size()) {
@@ -42,8 +43,8 @@ void AsyncShaders::AllocateWorkers() {
// Create workers
for (std::size_t i = 0; i < num_workers; i++) {
context_list.push_back(emu_window.CreateSharedContext());
- worker_threads.push_back(
- std::thread(&AsyncShaders::ShaderCompilerThread, this, context_list[i].get()));
+ worker_threads.emplace_back(&AsyncShaders::ShaderCompilerThread, this,
+ context_list[i].get());
}
}
@@ -73,11 +74,11 @@ void AsyncShaders::KillWorkers() {
worker_threads.clear();
}
-bool AsyncShaders::HasWorkQueued() {
+bool AsyncShaders::HasWorkQueued() const {
return !pending_queue.empty();
}
-bool AsyncShaders::HasCompletedWork() {
+bool AsyncShaders::HasCompletedWork() const {
std::shared_lock lock{completed_mutex};
return !finished_work.empty();
}
@@ -102,11 +103,10 @@ bool AsyncShaders::IsShaderAsync(const Tegra::GPU& gpu) const {
}
std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
- std::vector<AsyncShaders::Result> results;
+ std::vector<Result> results;
{
std::unique_lock lock{completed_mutex};
- results.assign(std::make_move_iterator(finished_work.begin()),
- std::make_move_iterator(finished_work.end()));
+ results = std::move(finished_work);
finished_work.clear();
}
return results;
@@ -115,11 +115,10 @@ std::vector<AsyncShaders::Result> AsyncShaders::GetCompletedWork() {
void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
Tegra::Engines::ShaderType shader_type, u64 uid,
std::vector<u64> code, std::vector<u64> code_b,
- u32 main_offset,
- VideoCommon::Shader::CompilerSettings compiler_settings,
- const VideoCommon::Shader::Registry& registry,
- VAddr cpu_addr) {
- WorkerParams params{
+ u32 main_offset, CompilerSettings compiler_settings,
+ const Registry& registry, VAddr cpu_addr) {
+ std::unique_lock lock(queue_mutex);
+ pending_queue.push({
.backend = device.UseAssemblyShaders() ? Backend::GLASM : Backend::OpenGL,
.device = &device,
.shader_type = shader_type,
@@ -130,9 +129,7 @@ void AsyncShaders::QueueOpenGLShader(const OpenGL::Device& device,
.compiler_settings = compiler_settings,
.registry = registry,
.cpu_address = cpu_addr,
- };
- std::unique_lock lock(queue_mutex);
- pending_queue.push(std::move(params));
+ });
cv.notify_one();
}
@@ -144,7 +141,8 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
std::vector<VkDescriptorSetLayoutBinding> bindings,
Vulkan::SPIRVProgram program,
Vulkan::GraphicsPipelineCacheKey key) {
- WorkerParams params{
+ std::unique_lock lock(queue_mutex);
+ pending_queue.push({
.backend = Backend::Vulkan,
.pp_cache = pp_cache,
.vk_device = &device,
@@ -152,13 +150,10 @@ void AsyncShaders::QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache,
.descriptor_pool = &descriptor_pool,
.update_descriptor_queue = &update_descriptor_queue,
.renderpass_cache = &renderpass_cache,
- .bindings = bindings,
- .program = program,
+ .bindings = std::move(bindings),
+ .program = std::move(program),
.key = key,
- };
-
- std::unique_lock lock(queue_mutex);
- pending_queue.push(std::move(params));
+ });
cv.notify_one();
}
diff --git a/src/video_core/shader/async_shaders.h b/src/video_core/shader/async_shaders.h
index d5ae814d5..7a99e1dc5 100644
--- a/src/video_core/shader/async_shaders.h
+++ b/src/video_core/shader/async_shaders.h
@@ -5,11 +5,21 @@
#pragma once
#include <condition_variable>
-#include <deque>
#include <memory>
#include <shared_mutex>
#include <thread>
-#include "common/bit_field.h"
+
+// This header includes both Vulkan and OpenGL headers, this has to be fixed
+// Unfortunately, including OpenGL will include Windows.h that defines macros that can cause issues.
+// Forcefully include glad early and undefine macros
+#include <glad/glad.h>
+#ifdef CreateEvent
+#undef CreateEvent
+#endif
+#ifdef CreateSemaphore
+#undef CreateSemaphore
+#endif
+
#include "common/common_types.h"
#include "video_core/renderer_opengl/gl_device.h"
#include "video_core/renderer_opengl/gl_resource_manager.h"
@@ -17,7 +27,6 @@
#include "video_core/renderer_vulkan/vk_device.h"
#include "video_core/renderer_vulkan/vk_pipeline_cache.h"
#include "video_core/renderer_vulkan/vk_scheduler.h"
-#include "video_core/renderer_vulkan/vk_update_descriptor.h"
namespace Core::Frontend {
class EmuWindow;
@@ -70,20 +79,20 @@ public:
void KillWorkers();
/// Check to see if any shaders have actually been compiled
- bool HasCompletedWork();
+ [[nodiscard]] bool HasCompletedWork() const;
/// Deduce if a shader can be build on another thread of MUST be built in sync. We cannot build
/// every shader async as some shaders are only built and executed once. We try to "guess" which
/// shader would be used only once
- bool IsShaderAsync(const Tegra::GPU& gpu) const;
+ [[nodiscard]] bool IsShaderAsync(const Tegra::GPU& gpu) const;
/// Pulls completed compiled shaders
- std::vector<Result> GetCompletedWork();
+ [[nodiscard]] std::vector<Result> GetCompletedWork();
void QueueOpenGLShader(const OpenGL::Device& device, Tegra::Engines::ShaderType shader_type,
u64 uid, std::vector<u64> code, std::vector<u64> code_b, u32 main_offset,
- VideoCommon::Shader::CompilerSettings compiler_settings,
- const VideoCommon::Shader::Registry& registry, VAddr cpu_addr);
+ CompilerSettings compiler_settings, const Registry& registry,
+ VAddr cpu_addr);
void QueueVulkanShader(Vulkan::VKPipelineCache* pp_cache, const Vulkan::VKDevice& device,
Vulkan::VKScheduler& scheduler,
@@ -97,7 +106,7 @@ private:
void ShaderCompilerThread(Core::Frontend::GraphicsContext* context);
/// Check our worker queue to see if we have any work queued already
- bool HasWorkQueued();
+ [[nodiscard]] bool HasWorkQueued() const;
struct WorkerParams {
Backend backend;
@@ -108,8 +117,8 @@ private:
std::vector<u64> code;
std::vector<u64> code_b;
u32 main_offset;
- VideoCommon::Shader::CompilerSettings compiler_settings;
- std::optional<VideoCommon::Shader::Registry> registry;
+ CompilerSettings compiler_settings;
+ std::optional<Registry> registry;
VAddr cpu_address;
// For Vulkan
@@ -125,13 +134,13 @@ private:
};
std::condition_variable cv;
- std::mutex queue_mutex;
- std::shared_mutex completed_mutex;
+ mutable std::mutex queue_mutex;
+ mutable std::shared_mutex completed_mutex;
std::atomic<bool> is_thread_exiting{};
std::vector<std::unique_ptr<Core::Frontend::GraphicsContext>> context_list;
std::vector<std::thread> worker_threads;
std::queue<WorkerParams> pending_queue;
- std::vector<AsyncShaders::Result> finished_work;
+ std::vector<Result> finished_work;
Core::Frontend::EmuWindow& emu_window;
};
diff --git a/src/video_core/shader/control_flow.cpp b/src/video_core/shader/control_flow.cpp
index 336397cdb..4c8971615 100644
--- a/src/video_core/shader/control_flow.cpp
+++ b/src/video_core/shader/control_flow.cpp
@@ -547,13 +547,13 @@ bool TryQuery(CFGRebuildState& state) {
gather_labels(q2.ssy_stack, state.ssy_labels, block);
gather_labels(q2.pbk_stack, state.pbk_labels, block);
if (std::holds_alternative<SingleBranch>(*block.branch)) {
- const auto branch = std::get_if<SingleBranch>(block.branch.get());
+ auto* branch = std::get_if<SingleBranch>(block.branch.get());
if (!branch->condition.IsUnconditional()) {
q2.address = block.end + 1;
state.queries.push_back(q2);
}
- Query conditional_query{q2};
+ auto& conditional_query = state.queries.emplace_back(q2);
if (branch->is_sync) {
if (branch->address == unassigned_branch) {
branch->address = conditional_query.ssy_stack.top();
@@ -567,21 +567,21 @@ bool TryQuery(CFGRebuildState& state) {
conditional_query.pbk_stack.pop();
}
conditional_query.address = branch->address;
- state.queries.push_back(std::move(conditional_query));
return true;
}
- const auto multi_branch = std::get_if<MultiBranch>(block.branch.get());
+
+ const auto* multi_branch = std::get_if<MultiBranch>(block.branch.get());
for (const auto& branch_case : multi_branch->branches) {
- Query conditional_query{q2};
+ auto& conditional_query = state.queries.emplace_back(q2);
conditional_query.address = branch_case.address;
- state.queries.push_back(std::move(conditional_query));
}
+
return true;
}
void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
- const auto get_expr = ([&](const Condition& cond) -> Expr {
- Expr result{};
+ const auto get_expr = [](const Condition& cond) -> Expr {
+ Expr result;
if (cond.cc != ConditionCode::T) {
result = MakeExpr<ExprCondCode>(cond.cc);
}
@@ -594,10 +594,10 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
}
Expr extra = MakeExpr<ExprPredicate>(pred);
if (negate) {
- extra = MakeExpr<ExprNot>(extra);
+ extra = MakeExpr<ExprNot>(std::move(extra));
}
if (result) {
- return MakeExpr<ExprAnd>(extra, result);
+ return MakeExpr<ExprAnd>(std::move(extra), std::move(result));
}
return extra;
}
@@ -605,9 +605,10 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
return result;
}
return MakeExpr<ExprBoolean>(true);
- });
+ };
+
if (std::holds_alternative<SingleBranch>(*branch_info)) {
- const auto branch = std::get_if<SingleBranch>(branch_info.get());
+ const auto* branch = std::get_if<SingleBranch>(branch_info.get());
if (branch->address < 0) {
if (branch->kill) {
mm.InsertReturn(get_expr(branch->condition), true);
@@ -619,7 +620,7 @@ void InsertBranch(ASTManager& mm, const BlockBranchInfo& branch_info) {
mm.InsertGoto(get_expr(branch->condition), branch->address);
return;
}
- const auto multi_branch = std::get_if<MultiBranch>(branch_info.get());
+ const auto* multi_branch = std::get_if<MultiBranch>(branch_info.get());
for (const auto& branch_case : multi_branch->branches) {
mm.InsertGoto(MakeExpr<ExprGprEqual>(multi_branch->gpr, branch_case.cmp_value),
branch_case.address);
diff --git a/src/video_core/shader/decode/arithmetic.cpp b/src/video_core/shader/decode/arithmetic.cpp
index 4db329fa5..afef5948d 100644
--- a/src/video_core/shader/decode/arithmetic.cpp
+++ b/src/video_core/shader/decode/arithmetic.cpp
@@ -137,7 +137,8 @@ u32 ShaderIR::DecodeArithmetic(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::FCMP_RR:
- case OpCode::Id::FCMP_RC: {
+ case OpCode::Id::FCMP_RC:
+ case OpCode::Id::FCMP_IMMR: {
UNIMPLEMENTED_IF(instr.fcmp.ftz == 0);
Node op_c = GetRegister(instr.gpr39);
Node comp = GetPredicateComparisonFloat(instr.fcmp.cond, std::move(op_c), Immediate(0.0f));
diff --git a/src/video_core/shader/decode/arithmetic_half.cpp b/src/video_core/shader/decode/arithmetic_half.cpp
index a276aee44..88103fede 100644
--- a/src/video_core/shader/decode/arithmetic_half.cpp
+++ b/src/video_core/shader/decode/arithmetic_half.cpp
@@ -53,6 +53,9 @@ u32 ShaderIR::DecodeArithmeticHalf(NodeBlock& bb, u32 pc) {
absolute_a = ((instr.value >> 44) & 1) != 0;
absolute_b = ((instr.value >> 54) & 1) != 0;
break;
+ default:
+ UNREACHABLE();
+ break;
}
Node op_a = UnpackHalfFloat(GetRegister(instr.gpr8), instr.alu_half.type_a);
diff --git a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
index 73880db0e..2a30aab2b 100644
--- a/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
+++ b/src/video_core/shader/decode/arithmetic_integer_immediate.cpp
@@ -28,23 +28,26 @@ u32 ShaderIR::DecodeArithmeticIntegerImmediate(NodeBlock& bb, u32 pc) {
case OpCode::Id::IADD32I: {
UNIMPLEMENTED_IF_MSG(instr.iadd32i.saturate, "IADD32I saturation is not implemented");
- op_a = GetOperandAbsNegInteger(op_a, false, instr.iadd32i.negate_a, true);
+ op_a = GetOperandAbsNegInteger(std::move(op_a), false, instr.iadd32i.negate_a != 0, true);
- const Node value = Operation(OperationCode::IAdd, PRECISE, op_a, op_b);
+ Node value = Operation(OperationCode::IAdd, PRECISE, std::move(op_a), std::move(op_b));
- SetInternalFlagsFromInteger(bb, value, instr.op_32.generates_cc);
- SetRegister(bb, instr.gpr0, value);
+ SetInternalFlagsFromInteger(bb, value, instr.op_32.generates_cc != 0);
+ SetRegister(bb, instr.gpr0, std::move(value));
break;
}
case OpCode::Id::LOP32I: {
- if (instr.alu.lop32i.invert_a)
- op_a = Operation(OperationCode::IBitwiseNot, NO_PRECISE, op_a);
+ if (instr.alu.lop32i.invert_a) {
+ op_a = Operation(OperationCode::IBitwiseNot, NO_PRECISE, std::move(op_a));
+ }
- if (instr.alu.lop32i.invert_b)
- op_b = Operation(OperationCode::IBitwiseNot, NO_PRECISE, op_b);
+ if (instr.alu.lop32i.invert_b) {
+ op_b = Operation(OperationCode::IBitwiseNot, NO_PRECISE, std::move(op_b));
+ }
- WriteLogicOperation(bb, instr.gpr0, instr.alu.lop32i.operation, op_a, op_b,
- PredicateResultMode::None, Pred::UnusedIndex, instr.op_32.generates_cc);
+ WriteLogicOperation(bb, instr.gpr0, instr.alu.lop32i.operation, std::move(op_a),
+ std::move(op_b), PredicateResultMode::None, Pred::UnusedIndex,
+ instr.op_32.generates_cc != 0);
break;
}
default:
@@ -58,14 +61,14 @@ u32 ShaderIR::DecodeArithmeticIntegerImmediate(NodeBlock& bb, u32 pc) {
void ShaderIR::WriteLogicOperation(NodeBlock& bb, Register dest, LogicOperation logic_op, Node op_a,
Node op_b, PredicateResultMode predicate_mode, Pred predicate,
bool sets_cc) {
- const Node result = [&]() {
+ Node result = [&] {
switch (logic_op) {
case LogicOperation::And:
- return Operation(OperationCode::IBitwiseAnd, PRECISE, op_a, op_b);
+ return Operation(OperationCode::IBitwiseAnd, PRECISE, std::move(op_a), std::move(op_b));
case LogicOperation::Or:
- return Operation(OperationCode::IBitwiseOr, PRECISE, op_a, op_b);
+ return Operation(OperationCode::IBitwiseOr, PRECISE, std::move(op_a), std::move(op_b));
case LogicOperation::Xor:
- return Operation(OperationCode::IBitwiseXor, PRECISE, op_a, op_b);
+ return Operation(OperationCode::IBitwiseXor, PRECISE, std::move(op_a), std::move(op_b));
case LogicOperation::PassB:
return op_b;
default:
@@ -84,8 +87,8 @@ void ShaderIR::WriteLogicOperation(NodeBlock& bb, Register dest, LogicOperation
return;
case PredicateResultMode::NotZero: {
// Set the predicate to true if the result is not zero.
- const Node compare = Operation(OperationCode::LogicalINotEqual, result, Immediate(0));
- SetPredicate(bb, static_cast<u64>(predicate), compare);
+ Node compare = Operation(OperationCode::LogicalINotEqual, std::move(result), Immediate(0));
+ SetPredicate(bb, static_cast<u64>(predicate), std::move(compare));
break;
}
default:
diff --git a/src/video_core/shader/decode/image.cpp b/src/video_core/shader/decode/image.cpp
index e75ca4fdb..618d309d2 100644
--- a/src/video_core/shader/decode/image.cpp
+++ b/src/video_core/shader/decode/image.cpp
@@ -119,6 +119,8 @@ ComponentType GetComponentType(Tegra::Engines::SamplerDescriptor descriptor,
return descriptor.r_type;
}
break;
+ default:
+ break;
}
UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
return ComponentType::FLOAT;
@@ -220,9 +222,10 @@ u32 GetComponentSize(TextureFormat format, std::size_t component) {
return (component == 0 || component == 1) ? 8 : 0;
case TextureFormat::G4R4:
return (component == 0 || component == 1) ? 4 : 0;
+ default:
+ UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
+ return 0;
}
- UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
- return 0;
}
std::size_t GetImageComponentMask(TextureFormat format) {
@@ -257,9 +260,10 @@ std::size_t GetImageComponentMask(TextureFormat format) {
case TextureFormat::R8:
case TextureFormat::R1:
return std::size_t{R};
+ default:
+ UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
+ return std::size_t{R | G | B | A};
}
- UNIMPLEMENTED_MSG("Texture format not implemented={}", format);
- return std::size_t{R | G | B | A};
}
std::size_t GetImageTypeNumCoordinates(Tegra::Shader::ImageType image_type) {
@@ -463,7 +467,10 @@ u32 ShaderIR::DecodeImage(NodeBlock& bb, u32 pc) {
return OperationCode::AtomicImageXor;
case Tegra::Shader::ImageAtomicOperation::Exch:
return OperationCode::AtomicImageExchange;
+ default:
+ break;
}
+ break;
default:
break;
}
diff --git a/src/video_core/shader/decode/memory.cpp b/src/video_core/shader/decode/memory.cpp
index e4739394d..e2bba88dd 100644
--- a/src/video_core/shader/decode/memory.cpp
+++ b/src/video_core/shader/decode/memory.cpp
@@ -386,7 +386,8 @@ u32 ShaderIR::DecodeMemory(NodeBlock& bb, u32 pc) {
break;
}
case OpCode::Id::RED: {
- UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32);
+ UNIMPLEMENTED_IF_MSG(instr.red.type != GlobalAtomicType::U32, "type={}",
+ static_cast<int>(instr.red.type.Value()));
const auto [real_address, base_address, descriptor] =
TrackGlobalMemory(bb, instr, true, true);
if (!real_address || !base_address) {
diff --git a/src/video_core/shader/decode/texture.cpp b/src/video_core/shader/decode/texture.cpp
index 29ebf65ba..02fdccd86 100644
--- a/src/video_core/shader/decode/texture.cpp
+++ b/src/video_core/shader/decode/texture.cpp
@@ -292,33 +292,36 @@ u32 ShaderIR::DecodeTexture(NodeBlock& bb, u32 pc) {
break;
}
- std::vector<Node> coords;
-
- // TODO: Add coordinates for different samplers once other texture types are implemented.
- switch (texture_type) {
- case TextureType::Texture1D:
- coords.push_back(GetRegister(instr.gpr8));
- break;
- case TextureType::Texture2D:
- coords.push_back(GetRegister(instr.gpr8.Value() + 0));
- coords.push_back(GetRegister(instr.gpr8.Value() + 1));
- break;
- default:
- UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<int>(texture_type));
+ const u64 base_index = is_array ? 1 : 0;
+ const u64 num_components = [texture_type] {
+ switch (texture_type) {
+ case TextureType::Texture1D:
+ return 1;
+ case TextureType::Texture2D:
+ return 2;
+ case TextureType::TextureCube:
+ return 3;
+ default:
+ UNIMPLEMENTED_MSG("Unhandled texture type {}", static_cast<int>(texture_type));
+ return 2;
+ }
+ }();
+ // TODO: What's the array component used for?
- // Fallback to interpreting as a 2D texture for now
- coords.push_back(GetRegister(instr.gpr8.Value() + 0));
- coords.push_back(GetRegister(instr.gpr8.Value() + 1));
+ std::vector<Node> coords;
+ coords.reserve(num_components);
+ for (u64 component = 0; component < num_components; ++component) {
+ coords.push_back(GetRegister(instr.gpr8.Value() + base_index + component));
}
+
u32 indexer = 0;
for (u32 element = 0; element < 2; ++element) {
if (!instr.tmml.IsComponentEnabled(element)) {
continue;
}
- auto params = coords;
MetaTexture meta{*sampler, {}, {}, {}, {}, {}, {}, {}, {}, element, index_var};
- const Node value = Operation(OperationCode::TextureQueryLod, meta, std::move(params));
- SetTemporary(bb, indexer++, value);
+ Node value = Operation(OperationCode::TextureQueryLod, meta, coords);
+ SetTemporary(bb, indexer++, std::move(value));
}
for (u32 i = 0; i < indexer; ++i) {
SetRegister(bb, instr.gpr0.Value() + i, GetTemporary(i));
@@ -553,7 +556,6 @@ Node4 ShaderIR::GetTextureCode(Instruction instr, TextureType texture_type,
const bool is_shadow = depth_compare != nullptr;
const bool is_bindless = bindless_reg.has_value();
- UNIMPLEMENTED_IF(texture_type == TextureType::TextureCube && is_array && is_shadow);
ASSERT_MSG(texture_type != TextureType::Texture3D || !is_array || !is_shadow,
"Illegal texture type");
@@ -763,7 +765,7 @@ Node4 ShaderIR::GetTld4Code(Instruction instr, TextureType texture_type, bool de
Node4 ShaderIR::GetTldCode(Tegra::Shader::Instruction instr) {
const auto texture_type{instr.tld.texture_type};
- const bool is_array{instr.tld.is_array};
+ const bool is_array{instr.tld.is_array != 0};
const bool lod_enabled{instr.tld.GetTextureProcessMode() == TextureProcessMode::LL};
const std::size_t coord_count{GetCoordCount(texture_type)};
diff --git a/src/video_core/shader/memory_util.cpp b/src/video_core/shader/memory_util.cpp
index 5071c83ca..e18ccba8e 100644
--- a/src/video_core/shader/memory_util.cpp
+++ b/src/video_core/shader/memory_util.cpp
@@ -16,11 +16,10 @@
namespace VideoCommon::Shader {
-GPUVAddr GetShaderAddress(Core::System& system,
+GPUVAddr GetShaderAddress(Tegra::Engines::Maxwell3D& maxwell3d,
Tegra::Engines::Maxwell3D::Regs::ShaderProgram program) {
- const auto& gpu{system.GPU().Maxwell3D()};
- const auto& shader_config{gpu.regs.shader_config[static_cast<std::size_t>(program)]};
- return gpu.regs.code_address.CodeAddress() + shader_config.offset;
+ const auto& shader_config{maxwell3d.regs.shader_config[static_cast<std::size_t>(program)]};
+ return maxwell3d.regs.code_address.CodeAddress() + shader_config.offset;
}
bool IsSchedInstruction(std::size_t offset, std::size_t main_offset) {
diff --git a/src/video_core/shader/memory_util.h b/src/video_core/shader/memory_util.h
index be90d24fd..4624d38e6 100644
--- a/src/video_core/shader/memory_util.h
+++ b/src/video_core/shader/memory_util.h
@@ -11,10 +11,6 @@
#include "video_core/engines/maxwell_3d.h"
#include "video_core/engines/shader_type.h"
-namespace Core {
-class System;
-}
-
namespace Tegra {
class MemoryManager;
}
@@ -27,7 +23,7 @@ constexpr u32 STAGE_MAIN_OFFSET = 10;
constexpr u32 KERNEL_MAIN_OFFSET = 0;
/// Gets the address for the specified shader stage program
-GPUVAddr GetShaderAddress(Core::System& system,
+GPUVAddr GetShaderAddress(Tegra::Engines::Maxwell3D& maxwell3d,
Tegra::Engines::Maxwell3D::Regs::ShaderProgram program);
/// Gets if the current instruction offset is a scheduler instruction
diff --git a/src/video_core/shader/registry.cpp b/src/video_core/shader/registry.cpp
index cdf274e54..148d91fcb 100644
--- a/src/video_core/shader/registry.cpp
+++ b/src/video_core/shader/registry.cpp
@@ -24,44 +24,45 @@ GraphicsInfo MakeGraphicsInfo(ShaderType shader_stage, ConstBufferEngineInterfac
if (shader_stage == ShaderType::Compute) {
return {};
}
- auto& graphics = static_cast<Tegra::Engines::Maxwell3D&>(engine);
-
- GraphicsInfo info;
- info.tfb_layouts = graphics.regs.tfb_layouts;
- info.tfb_varying_locs = graphics.regs.tfb_varying_locs;
- info.primitive_topology = graphics.regs.draw.topology;
- info.tessellation_primitive = graphics.regs.tess_mode.prim;
- info.tessellation_spacing = graphics.regs.tess_mode.spacing;
- info.tfb_enabled = graphics.regs.tfb_enabled;
- info.tessellation_clockwise = graphics.regs.tess_mode.cw;
- return info;
+
+ auto& graphics = dynamic_cast<Tegra::Engines::Maxwell3D&>(engine);
+
+ return {
+ .tfb_layouts = graphics.regs.tfb_layouts,
+ .tfb_varying_locs = graphics.regs.tfb_varying_locs,
+ .primitive_topology = graphics.regs.draw.topology,
+ .tessellation_primitive = graphics.regs.tess_mode.prim,
+ .tessellation_spacing = graphics.regs.tess_mode.spacing,
+ .tfb_enabled = graphics.regs.tfb_enabled != 0,
+ .tessellation_clockwise = graphics.regs.tess_mode.cw.Value() != 0,
+ };
}
ComputeInfo MakeComputeInfo(ShaderType shader_stage, ConstBufferEngineInterface& engine) {
if (shader_stage != ShaderType::Compute) {
return {};
}
- auto& compute = static_cast<Tegra::Engines::KeplerCompute&>(engine);
+
+ auto& compute = dynamic_cast<Tegra::Engines::KeplerCompute&>(engine);
const auto& launch = compute.launch_description;
- ComputeInfo info;
- info.workgroup_size = {launch.block_dim_x, launch.block_dim_y, launch.block_dim_z};
- info.local_memory_size_in_words = launch.local_pos_alloc;
- info.shared_memory_size_in_words = launch.shared_alloc;
- return info;
+ return {
+ .workgroup_size = {launch.block_dim_x, launch.block_dim_y, launch.block_dim_z},
+ .shared_memory_size_in_words = launch.shared_alloc,
+ .local_memory_size_in_words = launch.local_pos_alloc,
+ };
}
} // Anonymous namespace
-Registry::Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info)
+Registry::Registry(ShaderType shader_stage, const SerializedRegistryInfo& info)
: stage{shader_stage}, stored_guest_driver_profile{info.guest_driver_profile},
bound_buffer{info.bound_buffer}, graphics_info{info.graphics}, compute_info{info.compute} {}
-Registry::Registry(Tegra::Engines::ShaderType shader_stage,
- Tegra::Engines::ConstBufferEngineInterface& engine)
- : stage{shader_stage}, engine{&engine}, bound_buffer{engine.GetBoundBuffer()},
- graphics_info{MakeGraphicsInfo(shader_stage, engine)}, compute_info{MakeComputeInfo(
- shader_stage, engine)} {}
+Registry::Registry(ShaderType shader_stage, ConstBufferEngineInterface& engine_)
+ : stage{shader_stage}, engine{&engine_}, bound_buffer{engine_.GetBoundBuffer()},
+ graphics_info{MakeGraphicsInfo(shader_stage, engine_)}, compute_info{MakeComputeInfo(
+ shader_stage, engine_)} {}
Registry::~Registry() = default;
@@ -113,8 +114,7 @@ std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainSeparateSampler
return value;
}
-std::optional<Tegra::Engines::SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer,
- u32 offset) {
+std::optional<SamplerDescriptor> Registry::ObtainBindlessSampler(u32 buffer, u32 offset) {
const std::pair key = {buffer, offset};
const auto iter = bindless_samplers.find(key);
if (iter != bindless_samplers.end()) {
diff --git a/src/video_core/shader/registry.h b/src/video_core/shader/registry.h
index 231206765..4bebefdde 100644
--- a/src/video_core/shader/registry.h
+++ b/src/video_core/shader/registry.h
@@ -94,7 +94,7 @@ public:
explicit Registry(Tegra::Engines::ShaderType shader_stage, const SerializedRegistryInfo& info);
explicit Registry(Tegra::Engines::ShaderType shader_stage,
- Tegra::Engines::ConstBufferEngineInterface& engine);
+ Tegra::Engines::ConstBufferEngineInterface& engine_);
~Registry();
diff --git a/src/video_core/shader/track.cpp b/src/video_core/shader/track.cpp
index d5ed81442..6be3ea92b 100644
--- a/src/video_core/shader/track.cpp
+++ b/src/video_core/shader/track.cpp
@@ -205,12 +205,12 @@ std::optional<u32> ShaderIR::TrackImmediate(Node tracked, const NodeBlock& code,
const auto result = TrackRegister(&std::get<GprNode>(*tracked), code, cursor - 1);
const auto& found = result.first;
if (!found) {
- return {};
+ return std::nullopt;
}
if (const auto immediate = std::get_if<ImmediateNode>(&*found)) {
return immediate->GetValue();
}
- return {};
+ return std::nullopt;
}
std::pair<Node, s64> ShaderIR::TrackRegister(const GprNode* tracked, const NodeBlock& code,
diff --git a/src/video_core/texture_cache/surface_base.cpp b/src/video_core/texture_cache/surface_base.cpp
index dfcf36e0b..b44c09d71 100644
--- a/src/video_core/texture_cache/surface_base.cpp
+++ b/src/video_core/texture_cache/surface_base.cpp
@@ -115,20 +115,24 @@ std::optional<std::pair<u32, u32>> SurfaceBaseImpl::GetLayerMipmap(
if (gpu_addr == candidate_gpu_addr) {
return {{0, 0}};
}
+
if (candidate_gpu_addr < gpu_addr) {
- return {};
+ return std::nullopt;
}
+
const auto relative_address{static_cast<GPUVAddr>(candidate_gpu_addr - gpu_addr)};
const auto layer{static_cast<u32>(relative_address / layer_size)};
if (layer >= params.depth) {
- return {};
+ return std::nullopt;
}
+
const GPUVAddr mipmap_address = relative_address - layer_size * layer;
const auto mipmap_it =
Common::BinaryFind(mipmap_offsets.begin(), mipmap_offsets.end(), mipmap_address);
if (mipmap_it == mipmap_offsets.end()) {
- return {};
+ return std::nullopt;
}
+
const auto level{static_cast<u32>(std::distance(mipmap_offsets.begin(), mipmap_it))};
return std::make_pair(layer, level);
}
diff --git a/src/video_core/texture_cache/surface_params.cpp b/src/video_core/texture_cache/surface_params.cpp
index e614a92df..13dd16356 100644
--- a/src/video_core/texture_cache/surface_params.cpp
+++ b/src/video_core/texture_cache/surface_params.cpp
@@ -163,13 +163,11 @@ SurfaceParams SurfaceParams::CreateForImage(const FormatLookupTable& lookup_tabl
return params;
}
-SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
- const auto& regs = system.GPU().Maxwell3D().regs;
-
+SurfaceParams SurfaceParams::CreateForDepthBuffer(Tegra::Engines::Maxwell3D& maxwell3d) {
+ const auto& regs = maxwell3d.regs;
const auto block_depth = std::min(regs.zeta.memory_layout.block_depth.Value(), 5U);
const bool is_layered = regs.zeta_layers > 1 && block_depth == 0;
const auto pixel_format = PixelFormatFromDepthFormat(regs.zeta.format);
-
return {
.is_tiled = regs.zeta.memory_layout.type ==
Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear,
@@ -191,8 +189,9 @@ SurfaceParams SurfaceParams::CreateForDepthBuffer(Core::System& system) {
};
}
-SurfaceParams SurfaceParams::CreateForFramebuffer(Core::System& system, std::size_t index) {
- const auto& config{system.GPU().Maxwell3D().regs.rt[index]};
+SurfaceParams SurfaceParams::CreateForFramebuffer(Tegra::Engines::Maxwell3D& maxwell3d,
+ std::size_t index) {
+ const auto& config{maxwell3d.regs.rt[index]};
SurfaceParams params;
params.is_tiled =
config.memory_layout.type == Tegra::Engines::Maxwell3D::Regs::InvMemoryLayout::BlockLinear;
@@ -241,6 +240,7 @@ SurfaceParams SurfaceParams::CreateForFermiCopySurface(
.is_tiled = is_tiled,
.srgb_conversion = config.format == Tegra::RenderTargetFormat::B8G8R8A8_SRGB ||
config.format == Tegra::RenderTargetFormat::A8B8G8R8_SRGB,
+ .is_layered = false,
.block_width = is_tiled ? std::min(config.BlockWidth(), 5U) : 0U,
.block_height = is_tiled ? std::min(config.BlockHeight(), 5U) : 0U,
.block_depth = is_tiled ? std::min(config.BlockDepth(), 5U) : 0U,
diff --git a/src/video_core/texture_cache/surface_params.h b/src/video_core/texture_cache/surface_params.h
index 118aa689e..4466c3c34 100644
--- a/src/video_core/texture_cache/surface_params.h
+++ b/src/video_core/texture_cache/surface_params.h
@@ -33,10 +33,11 @@ public:
const VideoCommon::Shader::Image& entry);
/// Creates SurfaceCachedParams for a depth buffer configuration.
- static SurfaceParams CreateForDepthBuffer(Core::System& system);
+ static SurfaceParams CreateForDepthBuffer(Tegra::Engines::Maxwell3D& maxwell3d);
/// Creates SurfaceCachedParams from a framebuffer configuration.
- static SurfaceParams CreateForFramebuffer(Core::System& system, std::size_t index);
+ static SurfaceParams CreateForFramebuffer(Tegra::Engines::Maxwell3D& maxwell3d,
+ std::size_t index);
/// Creates SurfaceCachedParams from a Fermi2D surface configuration.
static SurfaceParams CreateForFermiCopySurface(
diff --git a/src/video_core/texture_cache/texture_cache.h b/src/video_core/texture_cache/texture_cache.h
index 96c4e4cc2..ea835c59f 100644
--- a/src/video_core/texture_cache/texture_cache.h
+++ b/src/video_core/texture_cache/texture_cache.h
@@ -135,8 +135,7 @@ public:
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
}
- const std::optional<VAddr> cpu_addr =
- system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
}
@@ -160,8 +159,7 @@ public:
if (!gpu_addr) {
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
}
- const std::optional<VAddr> cpu_addr =
- system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
return GetNullSurface(SurfaceParams::ExpectedTarget(entry));
}
@@ -183,11 +181,11 @@ public:
TView GetDepthBufferSurface(bool preserve_contents) {
std::lock_guard lock{mutex};
- auto& maxwell3d = system.GPU().Maxwell3D();
- if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer]) {
+ auto& dirty = maxwell3d.dirty;
+ if (!dirty.flags[VideoCommon::Dirty::ZetaBuffer]) {
return depth_buffer.view;
}
- maxwell3d.dirty.flags[VideoCommon::Dirty::ZetaBuffer] = false;
+ dirty.flags[VideoCommon::Dirty::ZetaBuffer] = false;
const auto& regs{maxwell3d.regs};
const auto gpu_addr{regs.zeta.Address()};
@@ -195,13 +193,12 @@ public:
SetEmptyDepthBuffer();
return {};
}
- const std::optional<VAddr> cpu_addr =
- system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
SetEmptyDepthBuffer();
return {};
}
- const auto depth_params{SurfaceParams::CreateForDepthBuffer(system)};
+ const auto depth_params{SurfaceParams::CreateForDepthBuffer(maxwell3d)};
auto surface_view = GetSurface(gpu_addr, *cpu_addr, depth_params, preserve_contents, true);
if (depth_buffer.target)
depth_buffer.target->MarkAsRenderTarget(false, NO_RT);
@@ -215,7 +212,6 @@ public:
TView GetColorBufferSurface(std::size_t index, bool preserve_contents) {
std::lock_guard lock{mutex};
ASSERT(index < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets);
- auto& maxwell3d = system.GPU().Maxwell3D();
if (!maxwell3d.dirty.flags[VideoCommon::Dirty::ColorBuffer0 + index]) {
return render_targets[index].view;
}
@@ -235,15 +231,14 @@ public:
return {};
}
- const std::optional<VAddr> cpu_addr =
- system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
SetEmptyColorBuffer(index);
return {};
}
auto surface_view =
- GetSurface(gpu_addr, *cpu_addr, SurfaceParams::CreateForFramebuffer(system, index),
+ GetSurface(gpu_addr, *cpu_addr, SurfaceParams::CreateForFramebuffer(maxwell3d, index),
preserve_contents, true);
if (render_targets[index].target) {
auto& surface = render_targets[index].target;
@@ -300,9 +295,8 @@ public:
const GPUVAddr dst_gpu_addr = dst_config.Address();
DeduceBestBlit(src_params, dst_params, src_gpu_addr, dst_gpu_addr);
- const auto& memory_manager = system.GPU().MemoryManager();
- const std::optional<VAddr> dst_cpu_addr = memory_manager.GpuToCpuAddress(dst_gpu_addr);
- const std::optional<VAddr> src_cpu_addr = memory_manager.GpuToCpuAddress(src_gpu_addr);
+ const std::optional<VAddr> dst_cpu_addr = gpu_memory.GpuToCpuAddress(dst_gpu_addr);
+ const std::optional<VAddr> src_cpu_addr = gpu_memory.GpuToCpuAddress(src_gpu_addr);
std::pair dst_surface = GetSurface(dst_gpu_addr, *dst_cpu_addr, dst_params, true, false);
TView src_surface = GetSurface(src_gpu_addr, *src_cpu_addr, src_params, true, false).second;
ImageBlit(src_surface, dst_surface.second, copy_config);
@@ -358,9 +352,11 @@ public:
}
protected:
- explicit TextureCache(Core::System& system, VideoCore::RasterizerInterface& rasterizer,
- bool is_astc_supported)
- : system{system}, is_astc_supported{is_astc_supported}, rasterizer{rasterizer} {
+ explicit TextureCache(VideoCore::RasterizerInterface& rasterizer_,
+ Tegra::Engines::Maxwell3D& maxwell3d_, Tegra::MemoryManager& gpu_memory_,
+ bool is_astc_supported_)
+ : is_astc_supported{is_astc_supported_}, rasterizer{rasterizer_}, maxwell3d{maxwell3d_},
+ gpu_memory{gpu_memory_} {
for (std::size_t i = 0; i < Tegra::Engines::Maxwell3D::Regs::NumRenderTargets; i++) {
SetEmptyColorBuffer(i);
}
@@ -395,7 +391,7 @@ protected:
virtual void BufferCopy(TSurface& src_surface, TSurface& dst_surface) = 0;
void ManageRenderTargetUnregister(TSurface& surface) {
- auto& dirty = system.GPU().Maxwell3D().dirty;
+ auto& dirty = maxwell3d.dirty;
const u32 index = surface->GetRenderTarget();
if (index == DEPTH_RT) {
dirty.flags[VideoCommon::Dirty::ZetaBuffer] = true;
@@ -408,8 +404,7 @@ protected:
void Register(TSurface surface) {
const GPUVAddr gpu_addr = surface->GetGpuAddr();
const std::size_t size = surface->GetSizeInBytes();
- const std::optional<VAddr> cpu_addr =
- system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
LOG_CRITICAL(HW_GPU, "Failed to register surface with unmapped gpu_address 0x{:016x}",
gpu_addr);
@@ -459,7 +454,6 @@ protected:
return new_surface;
}
- Core::System& system;
const bool is_astc_supported;
private:
@@ -954,8 +948,7 @@ private:
* @param params The parameters on the candidate surface.
**/
Deduction DeduceSurface(const GPUVAddr gpu_addr, const SurfaceParams& params) {
- const std::optional<VAddr> cpu_addr =
- system.GPU().MemoryManager().GpuToCpuAddress(gpu_addr);
+ const std::optional<VAddr> cpu_addr = gpu_memory.GpuToCpuAddress(gpu_addr);
if (!cpu_addr) {
Deduction result{};
@@ -1112,7 +1105,7 @@ private:
void LoadSurface(const TSurface& surface) {
staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes());
- surface->LoadBuffer(system.GPU().MemoryManager(), staging_cache);
+ surface->LoadBuffer(gpu_memory, staging_cache);
surface->UploadTexture(staging_cache.GetBuffer(0));
surface->MarkAsModified(false, Tick());
}
@@ -1123,7 +1116,7 @@ private:
}
staging_cache.GetBuffer(0).resize(surface->GetHostSizeInBytes());
surface->DownloadTexture(staging_cache.GetBuffer(0));
- surface->FlushBuffer(system.GPU().MemoryManager(), staging_cache);
+ surface->FlushBuffer(gpu_memory, staging_cache);
surface->MarkAsModified(false, Tick());
}
@@ -1253,6 +1246,8 @@ private:
}
VideoCore::RasterizerInterface& rasterizer;
+ Tegra::Engines::Maxwell3D& maxwell3d;
+ Tegra::MemoryManager& gpu_memory;
FormatLookupTable format_lookup_table;
FormatCompatibility format_compatibility;
diff --git a/src/video_core/video_core.cpp b/src/video_core/video_core.cpp
index 4e3a092c7..dd5cee4a1 100644
--- a/src/video_core/video_core.cpp
+++ b/src/video_core/video_core.cpp
@@ -21,14 +21,17 @@ namespace {
std::unique_ptr<VideoCore::RendererBase> CreateRenderer(
Core::System& system, Core::Frontend::EmuWindow& emu_window, Tegra::GPU& gpu,
std::unique_ptr<Core::Frontend::GraphicsContext> context) {
+ auto& telemetry_session = system.TelemetrySession();
+ auto& cpu_memory = system.Memory();
+
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
- return std::make_unique<OpenGL::RendererOpenGL>(system, emu_window, gpu,
- std::move(context));
+ return std::make_unique<OpenGL::RendererOpenGL>(telemetry_session, emu_window, cpu_memory,
+ gpu, std::move(context));
#ifdef HAS_VULKAN
case Settings::RendererBackend::Vulkan:
- return std::make_unique<Vulkan::RendererVulkan>(system, emu_window, gpu,
- std::move(context));
+ return std::make_unique<Vulkan::RendererVulkan>(telemetry_session, emu_window, cpu_memory,
+ gpu, std::move(context));
#endif
default:
return nullptr;
@@ -41,10 +44,11 @@ namespace VideoCore {
std::unique_ptr<Tegra::GPU> CreateGPU(Core::Frontend::EmuWindow& emu_window, Core::System& system) {
std::unique_ptr<Tegra::GPU> gpu;
+ const bool use_nvdec = Settings::values.use_nvdec_emulation.GetValue();
if (Settings::values.use_asynchronous_gpu_emulation.GetValue()) {
- gpu = std::make_unique<VideoCommon::GPUAsynch>(system);
+ gpu = std::make_unique<VideoCommon::GPUAsynch>(system, use_nvdec);
} else {
- gpu = std::make_unique<VideoCommon::GPUSynch>(system);
+ gpu = std::make_unique<VideoCommon::GPUSynch>(system, use_nvdec);
}
auto context = emu_window.CreateSharedContext();
diff --git a/src/web_service/CMakeLists.txt b/src/web_service/CMakeLists.txt
index 7e484b906..ae85a72ea 100644
--- a/src/web_service/CMakeLists.txt
+++ b/src/web_service/CMakeLists.txt
@@ -9,4 +9,4 @@ add_library(web_service STATIC
)
create_target_directory_groups(web_service)
-target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib lurlparser)
+target_link_libraries(web_service PRIVATE common nlohmann_json::nlohmann_json httplib)
diff --git a/src/web_service/web_backend.cpp b/src/web_service/web_backend.cpp
index 74e287045..67183e64c 100644
--- a/src/web_service/web_backend.cpp
+++ b/src/web_service/web_backend.cpp
@@ -7,7 +7,6 @@
#include <mutex>
#include <string>
-#include <LUrlParser.h>
#include <fmt/format.h>
#include <httplib.h>
@@ -19,9 +18,6 @@ namespace WebService {
constexpr std::array<const char, 1> API_VERSION{'1'};
-constexpr int HTTP_PORT = 80;
-constexpr int HTTPS_PORT = 443;
-
constexpr std::size_t TIMEOUT_SECONDS = 30;
struct Client::Impl {
@@ -67,28 +63,17 @@ struct Client::Impl {
const std::string& jwt = "", const std::string& username = "",
const std::string& token = "") {
if (cli == nullptr) {
- auto parsedUrl = LUrlParser::clParseURL::ParseURL(host);
- int port;
- if (parsedUrl.m_Scheme == "http") {
- if (!parsedUrl.GetPort(&port)) {
- port = HTTP_PORT;
- }
- cli = std::make_unique<httplib::Client>(parsedUrl.m_Host.c_str(), port);
- } else if (parsedUrl.m_Scheme == "https") {
- if (!parsedUrl.GetPort(&port)) {
- port = HTTPS_PORT;
- }
- cli = std::make_unique<httplib::SSLClient>(parsedUrl.m_Host.c_str(), port);
- } else {
- LOG_ERROR(WebService, "Bad URL scheme {}", parsedUrl.m_Scheme);
- return WebResult{WebResult::Code::InvalidURL, "Bad URL scheme", ""};
- }
+ cli = std::make_unique<httplib::Client>(host.c_str());
}
- if (cli == nullptr) {
- LOG_ERROR(WebService, "Invalid URL {}", host + path);
- return WebResult{WebResult::Code::InvalidURL, "Invalid URL", ""};
+
+ if (!cli->is_valid()) {
+ LOG_ERROR(WebService, "Client is invalid, skipping request!");
+ return {};
}
- cli->set_timeout_sec(TIMEOUT_SECONDS);
+
+ cli->set_connection_timeout(TIMEOUT_SECONDS);
+ cli->set_read_timeout(TIMEOUT_SECONDS);
+ cli->set_write_timeout(TIMEOUT_SECONDS);
httplib::Headers params;
if (!jwt.empty()) {
diff --git a/src/yuzu/CMakeLists.txt b/src/yuzu/CMakeLists.txt
index 656096c9f..b16b54032 100644
--- a/src/yuzu/CMakeLists.txt
+++ b/src/yuzu/CMakeLists.txt
@@ -9,6 +9,9 @@ add_executable(yuzu
about_dialog.cpp
about_dialog.h
aboutdialog.ui
+ applets/controller.cpp
+ applets/controller.h
+ applets/controller.ui
applets/error.cpp
applets/error.h
applets/profile_select.cpp
@@ -39,6 +42,9 @@ add_executable(yuzu
configuration/configure_debug.cpp
configuration/configure_debug.h
configuration/configure_debug.ui
+ configuration/configure_debug_controller.cpp
+ configuration/configure_debug_controller.h
+ configuration/configure_debug_controller.ui
configuration/configure_dialog.cpp
configuration/configure_dialog.h
configuration/configure_filesystem.cpp
@@ -59,12 +65,18 @@ add_executable(yuzu
configuration/configure_input.cpp
configuration/configure_input.h
configuration/configure_input.ui
+ configuration/configure_input_advanced.cpp
+ configuration/configure_input_advanced.h
+ configuration/configure_input_advanced.ui
configuration/configure_input_player.cpp
configuration/configure_input_player.h
configuration/configure_input_player.ui
- configuration/configure_input_simple.cpp
- configuration/configure_input_simple.h
- configuration/configure_input_simple.ui
+ configuration/configure_input_profile_dialog.cpp
+ configuration/configure_input_profile_dialog.h
+ configuration/configure_input_profile_dialog.ui
+ configuration/configure_motion_touch.cpp
+ configuration/configure_motion_touch.h
+ configuration/configure_motion_touch.ui
configuration/configure_mouse_advanced.cpp
configuration/configure_mouse_advanced.h
configuration/configure_mouse_advanced.ui
@@ -83,15 +95,24 @@ add_executable(yuzu
configuration/configure_system.cpp
configuration/configure_system.h
configuration/configure_system.ui
+ configuration/configure_touch_from_button.cpp
+ configuration/configure_touch_from_button.h
+ configuration/configure_touch_from_button.ui
configuration/configure_touchscreen_advanced.cpp
configuration/configure_touchscreen_advanced.h
configuration/configure_touchscreen_advanced.ui
+ configuration/configure_touch_widget.h
configuration/configure_ui.cpp
configuration/configure_ui.h
configuration/configure_ui.ui
+ configuration/configure_vibration.cpp
+ configuration/configure_vibration.h
+ configuration/configure_vibration.ui
configuration/configure_web.cpp
configuration/configure_web.h
configuration/configure_web.ui
+ configuration/input_profiles.cpp
+ configuration/input_profiles.h
debugger/console.cpp
debugger/console.h
debugger/profiler.cpp
@@ -248,10 +269,10 @@ endif()
if (MSVC)
include(CopyYuzuQt5Deps)
include(CopyYuzuSDLDeps)
- include(CopyYuzuUnicornDeps)
+ include(CopyYuzuFFmpegDeps)
copy_yuzu_Qt5_deps(yuzu)
copy_yuzu_SDL_deps(yuzu)
- copy_yuzu_unicorn_deps(yuzu)
+ copy_yuzu_FFmpeg_deps(yuzu)
endif()
if (NOT APPLE)
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; }
<signal>accepted()</signal>
<receiver>AboutDialog</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel">
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>AboutDialog</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel">
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/applets/controller.cpp b/src/yuzu/applets/controller.cpp
new file mode 100644
index 000000000..8ecfec770
--- /dev/null
+++ b/src/yuzu/applets/controller.cpp
@@ -0,0 +1,637 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <thread>
+
+#include "common/assert.h"
+#include "common/string_util.h"
+#include "core/core.h"
+#include "core/hle/lock.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+#include "ui_controller.h"
+#include "yuzu/applets/controller.h"
+#include "yuzu/configuration/configure_input.h"
+#include "yuzu/configuration/configure_input_profile_dialog.h"
+#include "yuzu/configuration/configure_vibration.h"
+#include "yuzu/configuration/input_profiles.h"
+#include "yuzu/main.h"
+
+namespace {
+
+constexpr std::size_t HANDHELD_INDEX = 8;
+
+constexpr std::array<std::array<bool, 4>, 8> led_patterns{{
+ {true, false, false, false},
+ {true, true, false, false},
+ {true, true, true, false},
+ {true, true, true, true},
+ {true, false, false, true},
+ {true, false, true, false},
+ {true, false, true, true},
+ {false, true, true, false},
+}};
+
+void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
+ bool connected) {
+ Core::System& system{Core::System::GetInstance()};
+
+ if (!system.IsPoweredOn()) {
+ return;
+ }
+
+ Service::SM::ServiceManager& sm = system.ServiceManager();
+
+ auto& npad =
+ sm.GetService<Service::HID::Hid>("hid")
+ ->GetAppletResource()
+ ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+
+ npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
+}
+
+// Returns true if the given controller type is compatible with the given parameters.
+bool IsControllerCompatible(Settings::ControllerType controller_type,
+ Core::Frontend::ControllerParameters parameters) {
+ switch (controller_type) {
+ case Settings::ControllerType::ProController:
+ return parameters.allow_pro_controller;
+ case Settings::ControllerType::DualJoyconDetached:
+ return parameters.allow_dual_joycons;
+ case Settings::ControllerType::LeftJoycon:
+ return parameters.allow_left_joycon;
+ case Settings::ControllerType::RightJoycon:
+ return parameters.allow_right_joycon;
+ case Settings::ControllerType::Handheld:
+ return parameters.enable_single_mode && parameters.allow_handheld;
+ default:
+ return false;
+ }
+}
+
+/// Maps the controller type combobox index to Controller Type enum
+constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
+ switch (index) {
+ case 0:
+ default:
+ return Settings::ControllerType::ProController;
+ case 1:
+ return Settings::ControllerType::DualJoyconDetached;
+ case 2:
+ return Settings::ControllerType::LeftJoycon;
+ case 3:
+ return Settings::ControllerType::RightJoycon;
+ case 4:
+ return Settings::ControllerType::Handheld;
+ }
+}
+
+/// Maps the Controller Type enum to controller type combobox index
+constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
+ switch (type) {
+ case Settings::ControllerType::ProController:
+ default:
+ return 0;
+ case Settings::ControllerType::DualJoyconDetached:
+ return 1;
+ case Settings::ControllerType::LeftJoycon:
+ return 2;
+ case Settings::ControllerType::RightJoycon:
+ return 3;
+ case Settings::ControllerType::Handheld:
+ return 4;
+ }
+}
+
+} // namespace
+
+QtControllerSelectorDialog::QtControllerSelectorDialog(
+ QWidget* parent, Core::Frontend::ControllerParameters parameters_,
+ InputCommon::InputSubsystem* input_subsystem_)
+ : QDialog(parent), ui(std::make_unique<Ui::QtControllerSelectorDialog>()),
+ parameters(std::move(parameters_)), input_subsystem{input_subsystem_},
+ input_profiles(std::make_unique<InputProfiles>()) {
+ ui->setupUi(this);
+
+ player_widgets = {
+ ui->widgetPlayer1, ui->widgetPlayer2, ui->widgetPlayer3, ui->widgetPlayer4,
+ ui->widgetPlayer5, ui->widgetPlayer6, ui->widgetPlayer7, ui->widgetPlayer8,
+ };
+
+ player_groupboxes = {
+ ui->groupPlayer1Connected, ui->groupPlayer2Connected, ui->groupPlayer3Connected,
+ ui->groupPlayer4Connected, ui->groupPlayer5Connected, ui->groupPlayer6Connected,
+ ui->groupPlayer7Connected, ui->groupPlayer8Connected,
+ };
+
+ connected_controller_icons = {
+ ui->controllerPlayer1, ui->controllerPlayer2, ui->controllerPlayer3, ui->controllerPlayer4,
+ ui->controllerPlayer5, ui->controllerPlayer6, ui->controllerPlayer7, ui->controllerPlayer8,
+ };
+
+ led_patterns_boxes = {{
+ {ui->checkboxPlayer1LED1, ui->checkboxPlayer1LED2, ui->checkboxPlayer1LED3,
+ ui->checkboxPlayer1LED4},
+ {ui->checkboxPlayer2LED1, ui->checkboxPlayer2LED2, ui->checkboxPlayer2LED3,
+ ui->checkboxPlayer2LED4},
+ {ui->checkboxPlayer3LED1, ui->checkboxPlayer3LED2, ui->checkboxPlayer3LED3,
+ ui->checkboxPlayer3LED4},
+ {ui->checkboxPlayer4LED1, ui->checkboxPlayer4LED2, ui->checkboxPlayer4LED3,
+ ui->checkboxPlayer4LED4},
+ {ui->checkboxPlayer5LED1, ui->checkboxPlayer5LED2, ui->checkboxPlayer5LED3,
+ ui->checkboxPlayer5LED4},
+ {ui->checkboxPlayer6LED1, ui->checkboxPlayer6LED2, ui->checkboxPlayer6LED3,
+ ui->checkboxPlayer6LED4},
+ {ui->checkboxPlayer7LED1, ui->checkboxPlayer7LED2, ui->checkboxPlayer7LED3,
+ ui->checkboxPlayer7LED4},
+ {ui->checkboxPlayer8LED1, ui->checkboxPlayer8LED2, ui->checkboxPlayer8LED3,
+ ui->checkboxPlayer8LED4},
+ }};
+
+ explain_text_labels = {
+ ui->labelPlayer1Explain, ui->labelPlayer2Explain, ui->labelPlayer3Explain,
+ ui->labelPlayer4Explain, ui->labelPlayer5Explain, ui->labelPlayer6Explain,
+ ui->labelPlayer7Explain, ui->labelPlayer8Explain,
+ };
+
+ emulated_controllers = {
+ ui->comboPlayer1Emulated, ui->comboPlayer2Emulated, ui->comboPlayer3Emulated,
+ ui->comboPlayer4Emulated, ui->comboPlayer5Emulated, ui->comboPlayer6Emulated,
+ ui->comboPlayer7Emulated, ui->comboPlayer8Emulated,
+ };
+
+ player_labels = {
+ ui->labelPlayer1, ui->labelPlayer2, ui->labelPlayer3, ui->labelPlayer4,
+ ui->labelPlayer5, ui->labelPlayer6, ui->labelPlayer7, ui->labelPlayer8,
+ };
+
+ connected_controller_labels = {
+ ui->labelConnectedPlayer1, ui->labelConnectedPlayer2, ui->labelConnectedPlayer3,
+ ui->labelConnectedPlayer4, ui->labelConnectedPlayer5, ui->labelConnectedPlayer6,
+ ui->labelConnectedPlayer7, ui->labelConnectedPlayer8,
+ };
+
+ connected_controller_checkboxes = {
+ ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
+ ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
+ ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
+ };
+
+ // Setup/load everything prior to setting up connections.
+ // This avoids unintentionally changing the states of elements while loading them in.
+ SetSupportedControllers();
+ DisableUnsupportedPlayers();
+ LoadConfiguration();
+
+ for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
+ SetExplainText(i);
+ UpdateControllerIcon(i);
+ UpdateLEDPattern(i);
+ UpdateBorderColor(i);
+
+ connect(player_groupboxes[i], &QGroupBox::toggled, [this, i](bool checked) {
+ if (checked) {
+ for (std::size_t index = 0; index <= i; ++index) {
+ connected_controller_checkboxes[index]->setChecked(checked);
+ }
+ } else {
+ for (std::size_t index = i; index < NUM_PLAYERS; ++index) {
+ connected_controller_checkboxes[index]->setChecked(checked);
+ }
+ }
+ });
+
+ connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
+ [this, i](int) {
+ UpdateControllerIcon(i);
+ UpdateControllerState(i);
+ UpdateLEDPattern(i);
+ CheckIfParametersMet();
+ });
+
+ connect(connected_controller_checkboxes[i], &QCheckBox::stateChanged, [this, i](int state) {
+ player_groupboxes[i]->setChecked(state == Qt::Checked);
+ UpdateControllerIcon(i);
+ UpdateControllerState(i);
+ UpdateLEDPattern(i);
+ UpdateBorderColor(i);
+ CheckIfParametersMet();
+ });
+
+ if (i == 0) {
+ connect(emulated_controllers[i], qOverload<int>(&QComboBox::currentIndexChanged),
+ [this](int index) {
+ UpdateDockedState(GetControllerTypeFromIndex(index) ==
+ Settings::ControllerType::Handheld);
+ });
+ }
+ }
+
+ connect(ui->vibrationButton, &QPushButton::clicked, this,
+ &QtControllerSelectorDialog::CallConfigureVibrationDialog);
+
+ connect(ui->inputConfigButton, &QPushButton::clicked, this,
+ &QtControllerSelectorDialog::CallConfigureInputProfileDialog);
+
+ connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
+ &QtControllerSelectorDialog::ApplyConfiguration);
+
+ // Enhancement: Check if the parameters have already been met before disconnecting controllers.
+ // If all the parameters are met AND only allows a single player,
+ // stop the constructor here as we do not need to continue.
+ if (CheckIfParametersMet() && parameters.enable_single_mode) {
+ return;
+ }
+
+ // If keep_controllers_connected is false, forcefully disconnect all controllers
+ if (!parameters.keep_controllers_connected) {
+ for (auto player : player_groupboxes) {
+ player->setChecked(false);
+ }
+ }
+
+ resize(0, 0);
+}
+
+QtControllerSelectorDialog::~QtControllerSelectorDialog() = default;
+
+int QtControllerSelectorDialog::exec() {
+ if (parameters_met && parameters.enable_single_mode) {
+ return QDialog::Accepted;
+ }
+ return QDialog::exec();
+}
+
+void QtControllerSelectorDialog::ApplyConfiguration() {
+ const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
+ Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
+ OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
+
+ Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
+ Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
+}
+
+void QtControllerSelectorDialog::LoadConfiguration() {
+ for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
+ const auto connected =
+ Settings::values.players.GetValue()[index].connected ||
+ (index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
+ player_groupboxes[index]->setChecked(connected);
+ connected_controller_checkboxes[index]->setChecked(connected);
+ emulated_controllers[index]->setCurrentIndex(
+ GetIndexFromControllerType(Settings::values.players.GetValue()[index].controller_type));
+ }
+
+ UpdateDockedState(Settings::values.players.GetValue()[HANDHELD_INDEX].connected);
+
+ ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
+ ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
+}
+
+void QtControllerSelectorDialog::CallConfigureVibrationDialog() {
+ ConfigureVibration dialog(this);
+
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
+ Qt::WindowSystemMenuHint);
+ dialog.setWindowModality(Qt::WindowModal);
+
+ if (dialog.exec() == QDialog::Accepted) {
+ dialog.ApplyConfiguration();
+ }
+}
+
+void QtControllerSelectorDialog::CallConfigureInputProfileDialog() {
+ ConfigureInputProfileDialog dialog(this, input_subsystem, input_profiles.get());
+
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
+ Qt::WindowSystemMenuHint);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.exec();
+}
+
+bool QtControllerSelectorDialog::CheckIfParametersMet() {
+ // Here, we check and validate the current configuration against all applicable parameters.
+ const auto num_connected_players = static_cast<int>(
+ std::count_if(player_groupboxes.begin(), player_groupboxes.end(),
+ [this](const QGroupBox* player) { return player->isChecked(); }));
+
+ const auto min_supported_players = parameters.enable_single_mode ? 1 : parameters.min_players;
+ const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
+
+ // First, check against the number of connected players.
+ if (num_connected_players < min_supported_players ||
+ num_connected_players > max_supported_players) {
+ parameters_met = false;
+ ui->buttonBox->setEnabled(parameters_met);
+ return parameters_met;
+ }
+
+ // Next, check against all connected controllers.
+ const auto all_controllers_compatible = [this] {
+ for (std::size_t index = 0; index < NUM_PLAYERS; ++index) {
+ // Skip controllers that are not used, we only care about the currently connected ones.
+ if (!player_groupboxes[index]->isChecked() || !player_groupboxes[index]->isEnabled()) {
+ continue;
+ }
+
+ const auto compatible = IsControllerCompatible(
+ GetControllerTypeFromIndex(emulated_controllers[index]->currentIndex()),
+ parameters);
+
+ // If any controller is found to be incompatible, return false early.
+ if (!compatible) {
+ return false;
+ }
+ }
+
+ // Reaching here means all currently connected controllers are compatible.
+ return true;
+ }();
+
+ parameters_met = all_controllers_compatible;
+ ui->buttonBox->setEnabled(parameters_met);
+ return parameters_met;
+}
+
+void QtControllerSelectorDialog::SetSupportedControllers() {
+ const QString theme = [] {
+ if (QIcon::themeName().contains(QStringLiteral("dark"))) {
+ return QStringLiteral("_dark");
+ } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
+ return QStringLiteral("_midnight");
+ } else {
+ return QString{};
+ }
+ }();
+
+ if (parameters.enable_single_mode && parameters.allow_handheld) {
+ ui->controllerSupported1->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_handheld%0); ").arg(theme));
+ } else {
+ ui->controllerSupported1->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_handheld%0_disabled); ").arg(theme));
+ }
+
+ if (parameters.allow_dual_joycons) {
+ ui->controllerSupported2->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ").arg(theme));
+ } else {
+ ui->controllerSupported2->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_dual_joycon%0_disabled); ").arg(theme));
+ }
+
+ if (parameters.allow_left_joycon) {
+ ui->controllerSupported3->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_joycon_left%0); ").arg(theme));
+ } else {
+ ui->controllerSupported3->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_joycon_left%0_disabled); ").arg(theme));
+ }
+
+ if (parameters.allow_right_joycon) {
+ ui->controllerSupported4->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_joycon_right%0); ").arg(theme));
+ } else {
+ ui->controllerSupported4->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_joycon_right%0_disabled); ").arg(theme));
+ }
+
+ if (parameters.allow_pro_controller) {
+ ui->controllerSupported5->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_pro_controller%0); ").arg(theme));
+ } else {
+ ui->controllerSupported5->setStyleSheet(
+ QStringLiteral("image: url(:/controller/applet_pro_controller%0_disabled); ")
+ .arg(theme));
+ }
+
+ // enable_single_mode overrides min_players and max_players.
+ if (parameters.enable_single_mode) {
+ ui->numberSupportedLabel->setText(QStringLiteral("1"));
+ return;
+ }
+
+ if (parameters.min_players == parameters.max_players) {
+ ui->numberSupportedLabel->setText(QStringLiteral("%1").arg(parameters.max_players));
+ } else {
+ ui->numberSupportedLabel->setText(
+ QStringLiteral("%1 - %2").arg(parameters.min_players).arg(parameters.max_players));
+ }
+}
+
+void QtControllerSelectorDialog::UpdateControllerIcon(std::size_t player_index) {
+ if (!player_groupboxes[player_index]->isChecked()) {
+ connected_controller_icons[player_index]->setStyleSheet(QString{});
+ player_labels[player_index]->show();
+ return;
+ }
+
+ const QString stylesheet = [this, player_index] {
+ switch (GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex())) {
+ case Settings::ControllerType::ProController:
+ return QStringLiteral("image: url(:/controller/applet_pro_controller%0); ");
+ case Settings::ControllerType::DualJoyconDetached:
+ return QStringLiteral("image: url(:/controller/applet_dual_joycon%0); ");
+ case Settings::ControllerType::LeftJoycon:
+ return QStringLiteral("image: url(:/controller/applet_joycon_left%0); ");
+ case Settings::ControllerType::RightJoycon:
+ return QStringLiteral("image: url(:/controller/applet_joycon_right%0); ");
+ case Settings::ControllerType::Handheld:
+ return QStringLiteral("image: url(:/controller/applet_handheld%0); ");
+ default:
+ return QString{};
+ }
+ }();
+
+ const QString theme = [] {
+ if (QIcon::themeName().contains(QStringLiteral("dark"))) {
+ return QStringLiteral("_dark");
+ } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
+ return QStringLiteral("_midnight");
+ } else {
+ return QString{};
+ }
+ }();
+
+ connected_controller_icons[player_index]->setStyleSheet(stylesheet.arg(theme));
+ player_labels[player_index]->hide();
+}
+
+void QtControllerSelectorDialog::UpdateControllerState(std::size_t player_index) {
+ auto& player = Settings::values.players.GetValue()[player_index];
+
+ const auto controller_type =
+ GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex());
+ const auto player_connected = player_groupboxes[player_index]->isChecked() &&
+ controller_type != Settings::ControllerType::Handheld;
+
+ if (player.controller_type == controller_type && player.connected == player_connected) {
+ // Set vibration devices in the event that the input device has changed.
+ ConfigureVibration::SetVibrationDevices(player_index);
+ return;
+ }
+
+ // Disconnect the controller first.
+ UpdateController(controller_type, player_index, false);
+
+ player.controller_type = controller_type;
+ player.connected = player_connected;
+
+ ConfigureVibration::SetVibrationDevices(player_index);
+
+ // Handheld
+ if (player_index == 0) {
+ auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
+ if (controller_type == Settings::ControllerType::Handheld) {
+ handheld = player;
+ }
+ handheld.connected = player_groupboxes[player_index]->isChecked() &&
+ controller_type == Settings::ControllerType::Handheld;
+ UpdateController(Settings::ControllerType::Handheld, 8, handheld.connected);
+ }
+
+ if (!player.connected) {
+ return;
+ }
+
+ // This emulates a delay between disconnecting and reconnecting controllers as some games
+ // do not respond to a change in controller type if it was instantaneous.
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(20ms);
+
+ UpdateController(controller_type, player_index, player_connected);
+}
+
+void QtControllerSelectorDialog::UpdateLEDPattern(std::size_t player_index) {
+ if (!player_groupboxes[player_index]->isChecked() ||
+ GetControllerTypeFromIndex(emulated_controllers[player_index]->currentIndex()) ==
+ Settings::ControllerType::Handheld) {
+ led_patterns_boxes[player_index][0]->setChecked(false);
+ led_patterns_boxes[player_index][1]->setChecked(false);
+ led_patterns_boxes[player_index][2]->setChecked(false);
+ led_patterns_boxes[player_index][3]->setChecked(false);
+ return;
+ }
+
+ led_patterns_boxes[player_index][0]->setChecked(led_patterns[player_index][0]);
+ led_patterns_boxes[player_index][1]->setChecked(led_patterns[player_index][1]);
+ led_patterns_boxes[player_index][2]->setChecked(led_patterns[player_index][2]);
+ led_patterns_boxes[player_index][3]->setChecked(led_patterns[player_index][3]);
+}
+
+void QtControllerSelectorDialog::UpdateBorderColor(std::size_t player_index) {
+ if (!parameters.enable_border_color ||
+ player_index >= static_cast<std::size_t>(parameters.max_players) ||
+ player_groupboxes[player_index]->styleSheet().contains(QStringLiteral("QGroupBox"))) {
+ return;
+ }
+
+ player_groupboxes[player_index]->setStyleSheet(
+ player_groupboxes[player_index]->styleSheet().append(
+ QStringLiteral("QGroupBox#groupPlayer%1Connected:checked "
+ "{ border: 1px solid rgba(%2, %3, %4, %5); }")
+ .arg(player_index + 1)
+ .arg(parameters.border_colors[player_index][0])
+ .arg(parameters.border_colors[player_index][1])
+ .arg(parameters.border_colors[player_index][2])
+ .arg(parameters.border_colors[player_index][3])));
+}
+
+void QtControllerSelectorDialog::SetExplainText(std::size_t player_index) {
+ if (!parameters.enable_explain_text ||
+ player_index >= static_cast<std::size_t>(parameters.max_players)) {
+ return;
+ }
+
+ explain_text_labels[player_index]->setText(QString::fromStdString(
+ Common::StringFromFixedZeroTerminatedBuffer(parameters.explain_text[player_index].data(),
+ parameters.explain_text[player_index].size())));
+}
+
+void QtControllerSelectorDialog::UpdateDockedState(bool is_handheld) {
+ // Disallow changing the console mode if the controller type is handheld.
+ ui->radioDocked->setEnabled(!is_handheld);
+ ui->radioUndocked->setEnabled(!is_handheld);
+
+ ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
+ ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
+
+ // Also force into undocked mode if the controller type is handheld.
+ if (is_handheld) {
+ ui->radioUndocked->setChecked(true);
+ }
+}
+
+void QtControllerSelectorDialog::DisableUnsupportedPlayers() {
+ const auto max_supported_players = parameters.enable_single_mode ? 1 : parameters.max_players;
+
+ switch (max_supported_players) {
+ case 0:
+ default:
+ UNREACHABLE();
+ return;
+ case 1:
+ ui->widgetSpacer->hide();
+ ui->widgetSpacer2->hide();
+ ui->widgetSpacer3->hide();
+ ui->widgetSpacer4->hide();
+ break;
+ case 2:
+ ui->widgetSpacer->hide();
+ ui->widgetSpacer2->hide();
+ ui->widgetSpacer3->hide();
+ break;
+ case 3:
+ ui->widgetSpacer->hide();
+ ui->widgetSpacer2->hide();
+ break;
+ case 4:
+ ui->widgetSpacer->hide();
+ break;
+ case 5:
+ case 6:
+ case 7:
+ case 8:
+ break;
+ }
+
+ for (std::size_t index = max_supported_players; index < NUM_PLAYERS; ++index) {
+ // Disconnect any unsupported players here and disable or hide them if applicable.
+ Settings::values.players.GetValue()[index].connected = false;
+ UpdateController(Settings::values.players.GetValue()[index].controller_type, index, false);
+ // Hide the player widgets when max_supported_controllers is less than or equal to 4.
+ if (max_supported_players <= 4) {
+ player_widgets[index]->hide();
+ }
+
+ // Disable and hide the following to prevent these from interaction.
+ player_widgets[index]->setDisabled(true);
+ connected_controller_checkboxes[index]->setDisabled(true);
+ connected_controller_labels[index]->hide();
+ connected_controller_checkboxes[index]->hide();
+ }
+}
+
+QtControllerSelector::QtControllerSelector(GMainWindow& parent) {
+ connect(this, &QtControllerSelector::MainWindowReconfigureControllers, &parent,
+ &GMainWindow::ControllerSelectorReconfigureControllers, Qt::QueuedConnection);
+ connect(&parent, &GMainWindow::ControllerSelectorReconfigureFinished, this,
+ &QtControllerSelector::MainWindowReconfigureFinished, Qt::QueuedConnection);
+}
+
+QtControllerSelector::~QtControllerSelector() = default;
+
+void QtControllerSelector::ReconfigureControllers(
+ std::function<void()> callback, const Core::Frontend::ControllerParameters& parameters) const {
+ this->callback = std::move(callback);
+ emit MainWindowReconfigureControllers(parameters);
+}
+
+void QtControllerSelector::MainWindowReconfigureFinished() {
+ // Acquire the HLE mutex
+ std::lock_guard lock(HLE::g_hle_lock);
+ callback();
+}
diff --git a/src/yuzu/applets/controller.h b/src/yuzu/applets/controller.h
new file mode 100644
index 000000000..4344e1dd0
--- /dev/null
+++ b/src/yuzu/applets/controller.h
@@ -0,0 +1,144 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <QDialog>
+#include "core/frontend/applets/controller.h"
+
+class GMainWindow;
+class QCheckBox;
+class QComboBox;
+class QDialogButtonBox;
+class QGroupBox;
+class QLabel;
+
+class InputProfiles;
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
+namespace Ui {
+class QtControllerSelectorDialog;
+}
+
+class QtControllerSelectorDialog final : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit QtControllerSelectorDialog(QWidget* parent,
+ Core::Frontend::ControllerParameters parameters_,
+ InputCommon::InputSubsystem* input_subsystem_);
+ ~QtControllerSelectorDialog() override;
+
+ int exec() override;
+
+private:
+ // Applies the current configuration.
+ void ApplyConfiguration();
+
+ // Loads the current input configuration into the frontend applet.
+ void LoadConfiguration();
+
+ // Initializes the "Configure Vibration" Dialog.
+ void CallConfigureVibrationDialog();
+
+ // Initializes the "Create Input Profile" Dialog.
+ void CallConfigureInputProfileDialog();
+
+ // Checks the current configuration against the given parameters.
+ // This sets and returns the value of parameters_met.
+ bool CheckIfParametersMet();
+
+ // Sets the controller icons for "Supported Controller Types".
+ void SetSupportedControllers();
+
+ // Updates the controller icons per player.
+ void UpdateControllerIcon(std::size_t player_index);
+
+ // Updates the controller state (type and connection status) per player.
+ void UpdateControllerState(std::size_t player_index);
+
+ // Updates the LED pattern per player.
+ void UpdateLEDPattern(std::size_t player_index);
+
+ // Updates the border color per player.
+ void UpdateBorderColor(std::size_t player_index);
+
+ // Sets the "Explain Text" per player.
+ void SetExplainText(std::size_t player_index);
+
+ // Updates the console mode.
+ void UpdateDockedState(bool is_handheld);
+
+ // Disables and disconnects unsupported players based on the given parameters.
+ void DisableUnsupportedPlayers();
+
+ std::unique_ptr<Ui::QtControllerSelectorDialog> ui;
+
+ // Parameters sent in from the backend HLE applet.
+ Core::Frontend::ControllerParameters parameters;
+
+ InputCommon::InputSubsystem* input_subsystem;
+
+ std::unique_ptr<InputProfiles> input_profiles;
+
+ // This is true if and only if all parameters are met. Otherwise, this is false.
+ // This determines whether the "OK" button can be clicked to exit the applet.
+ bool parameters_met{false};
+
+ static constexpr std::size_t NUM_PLAYERS = 8;
+
+ // Widgets encapsulating the groupboxes and comboboxes per player.
+ std::array<QWidget*, NUM_PLAYERS> player_widgets;
+
+ // Groupboxes encapsulating the controller icons and LED patterns per player.
+ std::array<QGroupBox*, NUM_PLAYERS> player_groupboxes;
+
+ // Icons for currently connected controllers/players.
+ std::array<QWidget*, NUM_PLAYERS> connected_controller_icons;
+
+ // Labels that represent the player numbers in place of the controller icons.
+ std::array<QLabel*, NUM_PLAYERS> player_labels;
+
+ // LED patterns for currently connected controllers/players.
+ std::array<std::array<QCheckBox*, 4>, NUM_PLAYERS> led_patterns_boxes;
+
+ // Labels representing additional information known as "Explain Text" per player.
+ std::array<QLabel*, NUM_PLAYERS> explain_text_labels;
+
+ // Comboboxes with a list of emulated controllers per player.
+ std::array<QComboBox*, NUM_PLAYERS> emulated_controllers;
+
+ // Labels representing the number of connected controllers
+ // above the "Connected Controllers" checkboxes.
+ std::array<QLabel*, NUM_PLAYERS> connected_controller_labels;
+
+ // Checkboxes representing the "Connected Controllers".
+ std::array<QCheckBox*, NUM_PLAYERS> connected_controller_checkboxes;
+};
+
+class QtControllerSelector final : public QObject, public Core::Frontend::ControllerApplet {
+ Q_OBJECT
+
+public:
+ explicit QtControllerSelector(GMainWindow& parent);
+ ~QtControllerSelector() override;
+
+ void ReconfigureControllers(
+ std::function<void()> callback,
+ const Core::Frontend::ControllerParameters& parameters) const override;
+
+signals:
+ void MainWindowReconfigureControllers(
+ const Core::Frontend::ControllerParameters& parameters) const;
+
+private:
+ void MainWindowReconfigureFinished();
+
+ mutable std::function<void()> callback;
+};
diff --git a/src/yuzu/applets/controller.ui b/src/yuzu/applets/controller.ui
new file mode 100644
index 000000000..c8cb6bcf3
--- /dev/null
+++ b/src/yuzu/applets/controller.ui
@@ -0,0 +1,2653 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>QtControllerSelectorDialog</class>
+ <widget class="QDialog" name="QtControllerSelectorDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>839</width>
+ <height>630</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Controller Applet</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout" stretch="0">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="mainControllerApplet" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2" stretch="0,3,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="topControllerApplet" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>10</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>10</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>10</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QWidget" name="controllersSupported" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_21">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="controllersSupportedLabel">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Supported Controller Types:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="controllerSupported1" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="controllerSupported2" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="controllerSupported3" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="controllerSupported4" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="controllerSupported5" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="playersSupported" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>70</width>
+ <height>70</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_20">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>16</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="maxSupportedLabel">
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="text">
+ <string>Players:</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="wordWrap">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="numberSupportedLabel">
+ <property name="font">
+ <font>
+ <pointsize>14</pointsize>
+ </font>
+ </property>
+ <property name="text">
+ <string>1 - 8</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="middleControllerApplet" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <item row="1" column="7">
+ <widget class="QWidget" name="widgetPlayer4" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_27">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer4Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_7" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer4" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_15">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer4">
+ <property name="text">
+ <string>P4</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player4LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer4LED1"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer4LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer4LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer4LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player4Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_39">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer4Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer4Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer4Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QWidget" name="widgetPlayer2" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_29">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer2Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer2" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_13">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer2">
+ <property name="text">
+ <string>P2</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player2LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer2LED1"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer2LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer2LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer2LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player2Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_37">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer2Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer2Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer2Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QWidget" name="widgetPlayer1" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_30">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer1Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer1" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_12">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer1">
+ <property name="text">
+ <string>P1</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player1LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer1LED1">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer1LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer1LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer1LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player1Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_36">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer1Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer1Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Handheld</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer1Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="8">
+ <widget class="QWidget" name="widgetSpacer2" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_31">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer8">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>25</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QWidget" name="widgetSpacer4" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_33">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer6">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="6">
+ <widget class="QWidget" name="widgetSpacer3" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_32">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer7">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="5">
+ <widget class="QWidget" name="widgetPlayer3" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_28">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer3Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_6" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer3" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_14">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer3">
+ <property name="text">
+ <string>P3</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player3LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer3LED1"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer3LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer3LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer3LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player3Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_38">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer3Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer3Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer3Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QWidget" name="widgetSpacer5" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>25</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_34">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletVerticalSpacer3">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>25</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="5">
+ <widget class="QWidget" name="widgetPlayer7" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_25">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer7Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_10" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer7" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_18">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer7">
+ <property name="text">
+ <string>P7</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player7LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer7LED1"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer7LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer7LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer7LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player7Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_42">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer7Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer7Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer7Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="7">
+ <widget class="QWidget" name="widgetPlayer8" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_26">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer8Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_11" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer8" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_19">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer8">
+ <property name="text">
+ <string>P8</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player8LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer8LED1"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer8LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer8LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer8LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player8Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_35">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer8Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer8Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer8Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="1">
+ <widget class="QWidget" name="widgetPlayer5" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_23">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer5Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_8" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer5" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_16">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer5">
+ <property name="text">
+ <string>P5</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player5LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer5LED1">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer5LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer5LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer5LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player5Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_40">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer5Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer5Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer5Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="6" column="3">
+ <widget class="QWidget" name="widgetPlayer6" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_24">
+ <property name="spacing">
+ <number>5</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupPlayer6Connected">
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>100</height>
+ </size>
+ </property>
+ <property name="title">
+ <string/>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_9" stretch="1,0">
+ <property name="spacing">
+ <number>7</number>
+ </property>
+ <property name="leftMargin">
+ <number>14</number>
+ </property>
+ <property name="topMargin">
+ <number>7</number>
+ </property>
+ <property name="rightMargin">
+ <number>14</number>
+ </property>
+ <property name="bottomMargin">
+ <number>4</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="controllerPlayer6" native="true">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_17">
+ <property name="topMargin">
+ <number>16</number>
+ </property>
+ <item alignment="Qt::AlignHCenter|Qt::AlignVCenter">
+ <widget class="QLabel" name="labelPlayer6">
+ <property name="text">
+ <string>P6</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QWidget" name="Player6LEDs" native="true">
+ <property name="enabled">
+ <bool>false</bool>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <property name="spacing">
+ <number>4</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer6LED1"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer6LED2"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer6LED3"/>
+ </item>
+ <item>
+ <widget class="QCheckBox" name="checkboxPlayer6LED4"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="Player6Explain" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>10</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>150</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_41">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QLabel" name="labelPlayer6Explain">
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer6Emulated">
+ <item>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="comboPlayer6Profile">
+ <item>
+ <property name="text">
+ <string>Use Current Config</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="10" column="1">
+ <widget class="QWidget" name="widgetSpacer" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>25</height>
+ </size>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_22">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletVerticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>25</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QWidget" name="widgetSpacer6" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QWidget" name="widgetSpacer7" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>25</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_16">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>25</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item row="2" column="1">
+ <widget class="QWidget" name="widgetSpacer9" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>25</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_17">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="controllerAppletVerticalSpacer2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>25</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="bottomControllerApplet" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="spacing">
+ <number>15</number>
+ </property>
+ <property name="leftMargin">
+ <number>15</number>
+ </property>
+ <property name="topMargin">
+ <number>8</number>
+ </property>
+ <property name="rightMargin">
+ <number>15</number>
+ </property>
+ <property name="bottomMargin">
+ <number>15</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="handheldGroup">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Console Mode</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>8</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QRadioButton" name="radioDocked">
+ <property name="text">
+ <string>Docked</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="radioUndocked">
+ <property name="text">
+ <string>Undocked</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroup">
+ <property name="title">
+ <string>Vibration</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="vibrationButton">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="motionGroup">
+ <property name="title">
+ <string>Motion</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="motionButton">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="inputConfigGroup">
+ <property name="title">
+ <string>Profiles</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="inputConfigButton">
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Create</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="connectedControllers" native="true">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item row="1" column="4">
+ <widget class="QCheckBox" name="checkboxPlayer4Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="labelControllers">
+ <property name="text">
+ <string>Controllers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="2">
+ <widget class="QCheckBox" name="checkboxPlayer2Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="labelConnectedPlayer1">
+ <property name="text">
+ <string>1</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QCheckBox" name="checkboxPlayer3Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="checkboxPlayer1Connected">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="checked">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="labelConnectedPlayer2">
+ <property name="text">
+ <string>2</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QLabel" name="labelConnectedPlayer4">
+ <property name="text">
+ <string>4</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="labelConnectedPlayer3">
+ <property name="text">
+ <string>3</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="labelConnected">
+ <property name="text">
+ <string>Connected</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="7">
+ <widget class="QCheckBox" name="checkboxPlayer7Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QLabel" name="labelConnectedPlayer5">
+ <property name="text">
+ <string>5</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="6">
+ <widget class="QCheckBox" name="checkboxPlayer6Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="7">
+ <widget class="QLabel" name="labelConnectedPlayer7">
+ <property name="text">
+ <string>7</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="5">
+ <widget class="QCheckBox" name="checkboxPlayer5Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="6">
+ <widget class="QLabel" name="labelConnectedPlayer6">
+ <property name="text">
+ <string>6</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="8">
+ <widget class="QLabel" name="labelConnectedPlayer8">
+ <property name="text">
+ <string>8</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="8">
+ <widget class="QCheckBox" name="checkboxPlayer8Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="controllerAppletHorizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignBottom">
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="enabled">
+ <bool>true</bool>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>QtControllerSelectorDialog</receiver>
+ <slot>accept()</slot>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/applets/profile_select.cpp b/src/yuzu/applets/profile_select.cpp
index dca8835ed..c9a2f8601 100644
--- a/src/yuzu/applets/profile_select.cpp
+++ b/src/yuzu/applets/profile_select.cpp
@@ -114,6 +114,15 @@ QtProfileSelectionDialog::QtProfileSelectionDialog(QWidget* parent)
QtProfileSelectionDialog::~QtProfileSelectionDialog() = default;
+int QtProfileSelectionDialog::exec() {
+ // Skip profile selection when there's only one.
+ if (profile_manager->GetUserCount() == 1) {
+ user_index = 0;
+ return QDialog::Accepted;
+ }
+ return QDialog::exec();
+}
+
void QtProfileSelectionDialog::accept() {
QDialog::accept();
}
diff --git a/src/yuzu/applets/profile_select.h b/src/yuzu/applets/profile_select.h
index cee886a77..29c33cca0 100644
--- a/src/yuzu/applets/profile_select.h
+++ b/src/yuzu/applets/profile_select.h
@@ -27,6 +27,7 @@ public:
explicit QtProfileSelectionDialog(QWidget* parent);
~QtProfileSelectionDialog() override;
+ int exec() override;
void accept() override;
void reject() override;
diff --git a/src/yuzu/bootmanager.cpp b/src/yuzu/bootmanager.cpp
index 8fc322b30..d62b0efc2 100644
--- a/src/yuzu/bootmanager.cpp
+++ b/src/yuzu/bootmanager.cpp
@@ -10,6 +10,7 @@
#include <QMessageBox>
#include <QPainter>
#include <QScreen>
+#include <QString>
#include <QStringList>
#include <QWindow>
@@ -30,6 +31,7 @@
#include "common/scope_exit.h"
#include "core/core.h"
#include "core/frontend/framebuffer_layout.h"
+#include "core/hle/kernel/process.h"
#include "core/settings.h"
#include "input_common/keyboard.h"
#include "input_common/main.h"
@@ -63,7 +65,8 @@ void EmuThread::run() {
emit LoadProgress(VideoCore::LoadCallbackStage::Prepare, 0, 0);
system.Renderer().Rasterizer().LoadDiskResources(
- stop_run, [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
+ system.CurrentProcess()->GetTitleID(), stop_run,
+ [this](VideoCore::LoadCallbackStage stage, std::size_t value, std::size_t total) {
emit LoadProgress(stage, value, total);
});
@@ -216,15 +219,6 @@ public:
virtual ~RenderWidget() = default;
- /// Called on the UI thread when this Widget is ready to draw
- /// Dervied classes can override this to draw the latest frame.
- virtual void Present() {}
-
- void paintEvent(QPaintEvent* event) override {
- Present();
- update();
- }
-
QPaintEngine* paintEngine() const override {
return nullptr;
}
@@ -243,20 +237,8 @@ public:
context = std::move(context_);
}
- void Present() override {
- if (!isVisible()) {
- return;
- }
-
- context->MakeCurrent();
- if (Core::System::GetInstance().Renderer().TryPresent(100)) {
- context->SwapBuffers();
- glFinish();
- }
- }
-
private:
- std::unique_ptr<Core::Frontend::GraphicsContext> context{};
+ std::unique_ptr<Core::Frontend::GraphicsContext> context;
};
#ifdef HAS_VULKAN
@@ -304,8 +286,9 @@ static Core::Frontend::EmuWindow::WindowSystemInfo GetWindowSystemInfo(QWindow*
return wsi;
}
-GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
- : QWidget(parent_), emu_thread(emu_thread_) {
+GRenderWindow::GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_)
+ : QWidget(parent), emu_thread(emu_thread_), input_subsystem{std::move(input_subsystem_)} {
setWindowTitle(QStringLiteral("yuzu %1 | %2-%3")
.arg(QString::fromUtf8(Common::g_build_name),
QString::fromUtf8(Common::g_scm_branch),
@@ -314,15 +297,15 @@ GRenderWindow::GRenderWindow(GMainWindow* parent_, EmuThread* emu_thread_)
auto layout = new QHBoxLayout(this);
layout->setMargin(0);
setLayout(layout);
- InputCommon::Init();
+ input_subsystem->Initialize();
this->setMouseTracking(true);
- connect(this, &GRenderWindow::FirstFrameDisplayed, parent_, &GMainWindow::OnLoadComplete);
+ connect(this, &GRenderWindow::FirstFrameDisplayed, parent, &GMainWindow::OnLoadComplete);
}
GRenderWindow::~GRenderWindow() {
- InputCommon::Shutdown();
+ input_subsystem->Shutdown();
}
void GRenderWindow::PollEvents() {
@@ -391,15 +374,20 @@ void GRenderWindow::closeEvent(QCloseEvent* event) {
}
void GRenderWindow::keyPressEvent(QKeyEvent* event) {
- InputCommon::GetKeyboard()->PressKey(event->key());
+ input_subsystem->GetKeyboard()->PressKey(event->key());
}
void GRenderWindow::keyReleaseEvent(QKeyEvent* event) {
- InputCommon::GetKeyboard()->ReleaseKey(event->key());
+ input_subsystem->GetKeyboard()->ReleaseKey(event->key());
}
void GRenderWindow::mousePressEvent(QMouseEvent* event) {
- // touch input is handled in TouchBeginEvent
+ if (!Settings::values.touchscreen.enabled) {
+ input_subsystem->GetKeyboard()->PressKey(event->button());
+ return;
+ }
+
+ // Touch input is handled in TouchBeginEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
@@ -409,13 +397,13 @@ void GRenderWindow::mousePressEvent(QMouseEvent* event) {
const auto [x, y] = ScaleTouch(pos);
this->TouchPressed(x, y);
} else if (event->button() == Qt::RightButton) {
- InputCommon::GetMotionEmu()->BeginTilt(pos.x(), pos.y());
+ input_subsystem->GetMotionEmu()->BeginTilt(pos.x(), pos.y());
}
QWidget::mousePressEvent(event);
}
void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
- // touch input is handled in TouchUpdateEvent
+ // Touch input is handled in TouchUpdateEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
@@ -423,12 +411,17 @@ void GRenderWindow::mouseMoveEvent(QMouseEvent* event) {
auto pos = event->pos();
const auto [x, y] = ScaleTouch(pos);
this->TouchMoved(x, y);
- InputCommon::GetMotionEmu()->Tilt(pos.x(), pos.y());
+ input_subsystem->GetMotionEmu()->Tilt(pos.x(), pos.y());
QWidget::mouseMoveEvent(event);
}
void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
- // touch input is handled in TouchEndEvent
+ if (!Settings::values.touchscreen.enabled) {
+ input_subsystem->GetKeyboard()->ReleaseKey(event->button());
+ return;
+ }
+
+ // Touch input is handled in TouchEndEvent
if (event->source() == Qt::MouseEventSynthesizedBySystem) {
return;
}
@@ -436,7 +429,7 @@ void GRenderWindow::mouseReleaseEvent(QMouseEvent* event) {
if (event->button() == Qt::LeftButton) {
this->TouchReleased();
} else if (event->button() == Qt::RightButton) {
- InputCommon::GetMotionEmu()->EndTilt();
+ input_subsystem->GetMotionEmu()->EndTilt();
}
}
@@ -451,7 +444,7 @@ void GRenderWindow::TouchUpdateEvent(const QTouchEvent* event) {
int active_points = 0;
// average all active touch points
- for (const auto tp : event->touchPoints()) {
+ for (const auto& tp : event->touchPoints()) {
if (tp.state() & (Qt::TouchPointPressed | Qt::TouchPointMoved | Qt::TouchPointStationary)) {
active_points++;
pos += tp.pos();
@@ -485,7 +478,7 @@ bool GRenderWindow::event(QEvent* event) {
void GRenderWindow::focusOutEvent(QFocusEvent* event) {
QWidget::focusOutEvent(event);
- InputCommon::GetKeyboard()->ReleaseAllKeys();
+ input_subsystem->GetKeyboard()->ReleaseAllKeys();
}
void GRenderWindow::resizeEvent(QResizeEvent* event) {
@@ -621,19 +614,33 @@ bool GRenderWindow::LoadOpenGL() {
auto context = CreateSharedContext();
auto scope = context->Acquire();
if (!gladLoadGL()) {
- QMessageBox::critical(this, tr("Error while initializing OpenGL 4.3!"),
- tr("Your GPU may not support OpenGL 4.3, or you do not have the "
- "latest graphics driver."));
+ QMessageBox::warning(
+ this, tr("Error while initializing OpenGL!"),
+ tr("Your GPU may not support OpenGL, or you do not have the latest graphics driver."));
+ return false;
+ }
+
+ const QString renderer =
+ QString::fromUtf8(reinterpret_cast<const char*>(glGetString(GL_RENDERER)));
+
+ if (!GLAD_GL_VERSION_4_3) {
+ LOG_ERROR(Frontend, "GPU does not support OpenGL 4.3: {}", renderer.toStdString());
+ QMessageBox::warning(this, tr("Error while initializing OpenGL 4.3!"),
+ tr("Your GPU may not support OpenGL 4.3, or you do not have the "
+ "latest graphics driver.<br><br>GL Renderer:<br>%1")
+ .arg(renderer));
return false;
}
QStringList unsupported_gl_extensions = GetUnsupportedGLExtensions();
if (!unsupported_gl_extensions.empty()) {
- QMessageBox::critical(
+ QMessageBox::warning(
this, tr("Error while initializing OpenGL!"),
tr("Your GPU may not support one or more required OpenGL extensions. Please ensure you "
- "have the latest graphics driver.<br><br>Unsupported extensions:<br>") +
- unsupported_gl_extensions.join(QStringLiteral("<br>")));
+ "have the latest graphics driver.<br><br>GL Renderer:<br>%1<br><br>Unsupported "
+ "extensions:<br>%2")
+ .arg(renderer)
+ .arg(unsupported_gl_extensions.join(QStringLiteral("<br>"))));
return false;
}
return true;
@@ -663,8 +670,13 @@ QStringList GRenderWindow::GetUnsupportedGLExtensions() const {
if (!GLAD_GL_ARB_depth_buffer_float)
unsupported_ext.append(QStringLiteral("ARB_depth_buffer_float"));
- for (const QString& ext : unsupported_ext)
- LOG_CRITICAL(Frontend, "Unsupported GL extension: {}", ext.toStdString());
+ if (!unsupported_ext.empty()) {
+ LOG_ERROR(Frontend, "GPU does not support all required extensions: {}",
+ glGetString(GL_RENDERER));
+ }
+ for (const QString& ext : unsupported_ext) {
+ LOG_ERROR(Frontend, "Unsupported GL extension: {}", ext.toStdString());
+ }
return unsupported_ext;
}
diff --git a/src/yuzu/bootmanager.h b/src/yuzu/bootmanager.h
index 6c59b4d5c..ca35cf831 100644
--- a/src/yuzu/bootmanager.h
+++ b/src/yuzu/bootmanager.h
@@ -6,6 +6,7 @@
#include <atomic>
#include <condition_variable>
+#include <memory>
#include <mutex>
#include <QImage>
@@ -23,6 +24,10 @@ class QKeyEvent;
class QTouchEvent;
class QStringList;
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace VideoCore {
enum class LoadCallbackStage;
}
@@ -121,7 +126,8 @@ class GRenderWindow : public QWidget, public Core::Frontend::EmuWindow {
Q_OBJECT
public:
- GRenderWindow(GMainWindow* parent, EmuThread* emu_thread);
+ explicit GRenderWindow(GMainWindow* parent, EmuThread* emu_thread_,
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem_);
~GRenderWindow() override;
// EmuWindow implementation.
@@ -183,6 +189,7 @@ private:
QStringList GetUnsupportedGLExtensions() const;
EmuThread* emu_thread;
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
// Main context that will be shared with all other contexts that are requested.
// If this is used in a shared context setting, then this should not be used directly, but
diff --git a/src/yuzu/configuration/config.cpp b/src/yuzu/configuration/config.cpp
index 1d4816d34..3c423a271 100644
--- a/src/yuzu/configuration/config.cpp
+++ b/src/yuzu/configuration/config.cpp
@@ -5,8 +5,8 @@
#include <array>
#include <QKeySequence>
#include <QSettings>
+#include "common/common_paths.h"
#include "common/file_util.h"
-#include "configure_input_simple.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/hid/controllers/npad.h"
#include "input_common/main.h"
@@ -15,14 +15,10 @@
namespace FS = Common::FS;
-Config::Config(const std::string& config_file, bool is_global) {
- // TODO: Don't hardcode the path; let the frontend decide where to put the config files.
- qt_config_loc = FS::GetUserPath(FS::UserPath::ConfigDir) + config_file;
- FS::CreateFullPath(qt_config_loc);
- qt_config =
- std::make_unique<QSettings>(QString::fromStdString(qt_config_loc), QSettings::IniFormat);
- global = is_global;
- Reload();
+Config::Config(const std::string& config_name, ConfigType config_type) : type(config_type) {
+ global = config_type == ConfigType::GlobalConfig;
+
+ Initialize(config_name);
}
Config::~Config() {
@@ -32,29 +28,36 @@ Config::~Config() {
}
const std::array<int, Settings::NativeButton::NumButtons> Config::default_buttons = {
- Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
- Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
- Qt::Key_H, Qt::Key_G, Qt::Key_Left, Qt::Key_Up, Qt::Key_Right, Qt::Key_Down, Qt::Key_J,
- Qt::Key_I, Qt::Key_L, Qt::Key_K, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
+ Qt::Key_A, Qt::Key_S, Qt::Key_Z, Qt::Key_X, Qt::Key_3, Qt::Key_4, Qt::Key_Q,
+ Qt::Key_W, Qt::Key_1, Qt::Key_2, Qt::Key_N, Qt::Key_M, Qt::Key_F, Qt::Key_T,
+ Qt::Key_H, Qt::Key_G, Qt::Key_D, Qt::Key_C, Qt::Key_B, Qt::Key_V,
+};
+
+const std::array<int, Settings::NativeMotion::NumMotions> Config::default_motions = {
+ Qt::Key_7,
+ Qt::Key_8,
};
-const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
+const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> Config::default_analogs{{
{
Qt::Key_Up,
Qt::Key_Down,
Qt::Key_Left,
Qt::Key_Right,
- Qt::Key_E,
},
{
Qt::Key_I,
Qt::Key_K,
Qt::Key_J,
Qt::Key_L,
- Qt::Key_R,
},
}};
+const std::array<int, 2> Config::default_stick_mod = {
+ Qt::Key_E,
+ Qt::Key_R,
+};
+
const std::array<int, Settings::NativeMouseButton::NumMouseButtons> Config::default_mouse_buttons =
{
Qt::Key_BracketLeft, Qt::Key_BracketRight, Qt::Key_Apostrophe, Qt::Key_Minus, Qt::Key_Equal,
@@ -236,76 +239,154 @@ const std::array<UISettings::Shortcut, 16> Config::default_hotkeys{{
}};
// clang-format on
-void Config::ReadPlayerValues() {
- for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
- auto& player = Settings::values.players[p];
+void Config::Initialize(const std::string& config_name) {
+ switch (type) {
+ case ConfigType::GlobalConfig:
+ qt_config_loc = fmt::format("{}" DIR_SEP "{}.ini", FS::GetUserPath(FS::UserPath::ConfigDir),
+ config_name);
+ FS::CreateFullPath(qt_config_loc);
+ qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
+ QSettings::IniFormat);
+ Reload();
+ break;
+ case ConfigType::PerGameConfig:
+ qt_config_loc = fmt::format("{}custom" DIR_SEP "{}.ini",
+ FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
+ FS::CreateFullPath(qt_config_loc);
+ qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
+ QSettings::IniFormat);
+ Reload();
+ break;
+ case ConfigType::InputProfile:
+ qt_config_loc = fmt::format("{}input" DIR_SEP "{}.ini",
+ FS::GetUserPath(FS::UserPath::ConfigDir), config_name);
+ FS::CreateFullPath(qt_config_loc);
+ qt_config = std::make_unique<QSettings>(QString::fromStdString(qt_config_loc),
+ QSettings::IniFormat);
+ break;
+ }
+}
+
+void Config::ReadPlayerValue(std::size_t player_index) {
+ const QString player_prefix = [this, player_index] {
+ if (type == ConfigType::InputProfile) {
+ return QString{};
+ } else {
+ return QStringLiteral("player_%1_").arg(player_index);
+ }
+ }();
+ auto& player = Settings::values.players.GetValue()[player_index];
+
+ if (player_prefix.isEmpty()) {
+ const auto controller = static_cast<Settings::ControllerType>(
+ qt_config
+ ->value(QStringLiteral("%1type").arg(player_prefix),
+ static_cast<u8>(Settings::ControllerType::ProController))
+ .toUInt());
+
+ if (controller == Settings::ControllerType::LeftJoycon ||
+ controller == Settings::ControllerType::RightJoycon) {
+ player.controller_type = controller;
+ }
+ } else {
player.connected =
- ReadSetting(QStringLiteral("player_%1_connected").arg(p), false).toBool();
+ ReadSetting(QStringLiteral("%1connected").arg(player_prefix), player_index == 0)
+ .toBool();
- player.type = static_cast<Settings::ControllerType>(
+ player.controller_type = static_cast<Settings::ControllerType>(
qt_config
- ->value(QStringLiteral("player_%1_type").arg(p),
- static_cast<u8>(Settings::ControllerType::DualJoycon))
+ ->value(QStringLiteral("%1type").arg(player_prefix),
+ static_cast<u8>(Settings::ControllerType::ProController))
.toUInt());
+ player.vibration_enabled =
+ qt_config->value(QStringLiteral("%1vibration_enabled").arg(player_prefix), true)
+ .toBool();
+
+ player.vibration_strength =
+ qt_config->value(QStringLiteral("%1vibration_strength").arg(player_prefix), 100)
+ .toInt();
+
player.body_color_left = qt_config
- ->value(QStringLiteral("player_%1_body_color_left").arg(p),
+ ->value(QStringLiteral("%1body_color_left").arg(player_prefix),
Settings::JOYCON_BODY_NEON_BLUE)
.toUInt();
- player.body_color_right = qt_config
- ->value(QStringLiteral("player_%1_body_color_right").arg(p),
- Settings::JOYCON_BODY_NEON_RED)
- .toUInt();
- player.button_color_left = qt_config
- ->value(QStringLiteral("player_%1_button_color_left").arg(p),
- Settings::JOYCON_BUTTONS_NEON_BLUE)
- .toUInt();
+ player.body_color_right =
+ qt_config
+ ->value(QStringLiteral("%1body_color_right").arg(player_prefix),
+ Settings::JOYCON_BODY_NEON_RED)
+ .toUInt();
+ player.button_color_left =
+ qt_config
+ ->value(QStringLiteral("%1button_color_left").arg(player_prefix),
+ Settings::JOYCON_BUTTONS_NEON_BLUE)
+ .toUInt();
player.button_color_right =
qt_config
- ->value(QStringLiteral("player_%1_button_color_right").arg(p),
+ ->value(QStringLiteral("%1button_color_right").arg(player_prefix),
Settings::JOYCON_BUTTONS_NEON_RED)
.toUInt();
+ }
- for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
- const std::string default_param =
- InputCommon::GenerateKeyboardParam(default_buttons[i]);
- auto& player_buttons = player.buttons[i];
-
- player_buttons = qt_config
- ->value(QStringLiteral("player_%1_").arg(p) +
- QString::fromUtf8(Settings::NativeButton::mapping[i]),
- QString::fromStdString(default_param))
- .toString()
- .toStdString();
- if (player_buttons.empty()) {
- player_buttons = default_param;
- }
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ auto& player_buttons = player.buttons[i];
+
+ player_buttons = qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (player_buttons.empty()) {
+ player_buttons = default_param;
}
+ }
- for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
- const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
- default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
- auto& player_analogs = player.analogs[i];
-
- player_analogs = qt_config
- ->value(QStringLiteral("player_%1_").arg(p) +
- QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
- QString::fromStdString(default_param))
- .toString()
- .toStdString();
- if (player_analogs.empty()) {
- player_analogs = default_param;
- }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
+ auto& player_analogs = player.analogs[i];
+
+ player_analogs = qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (player_analogs.empty()) {
+ player_analogs = default_param;
}
}
- std::stable_partition(
- Settings::values.players.begin(),
- Settings::values.players.begin() +
- Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
- [](const auto& player) { return player.connected; });
+ for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
+ auto& player_vibrations = player.vibrations[i];
+
+ player_vibrations =
+ qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeVibration::mapping[i]),
+ QString{})
+ .toString()
+ .toStdString();
+ }
+
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
+ auto& player_motions = player.motions[i];
+
+ player_motions = qt_config
+ ->value(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromUtf8(Settings::NativeMotion::mapping[i]),
+ QString::fromStdString(default_param))
+ .toString()
+ .toStdString();
+ if (player_motions.empty()) {
+ player_motions = default_param;
+ }
+ }
}
void Config::ReadDebugValues() {
@@ -330,7 +411,7 @@ void Config::ReadDebugValues() {
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
auto& debug_pad_analogs = Settings::values.debug_pad_analogs[i];
debug_pad_analogs = qt_config
@@ -397,13 +478,6 @@ void Config::ReadTouchscreenValues() {
ReadSetting(QStringLiteral("touchscreen_diameter_y"), 15).toUInt();
}
-void Config::ApplyDefaultProfileIfInputInvalid() {
- if (!std::any_of(Settings::values.players.begin(), Settings::values.players.end(),
- [](const Settings::PlayerInput& in) { return in.connected; })) {
- ApplyInputProfileConfiguration(UISettings::values.profile_index);
- }
-}
-
void Config::ReadAudioValues() {
qt_config->beginGroup(QStringLiteral("Audio"));
@@ -427,17 +501,73 @@ void Config::ReadAudioValues() {
void Config::ReadControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
- ReadPlayerValues();
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
+ ReadPlayerValue(p);
+ }
ReadDebugValues();
ReadKeyboardValues();
ReadMouseValues();
ReadTouchscreenValues();
+ ReadMotionTouchValues();
+
+ ReadSettingGlobal(Settings::values.use_docked_mode, QStringLiteral("use_docked_mode"), false);
+ ReadSettingGlobal(Settings::values.vibration_enabled, QStringLiteral("vibration_enabled"),
+ true);
+ ReadSettingGlobal(Settings::values.enable_accurate_vibrations,
+ QStringLiteral("enable_accurate_vibrations"), false);
+ ReadSettingGlobal(Settings::values.motion_enabled, QStringLiteral("motion_enabled"), true);
+
+ qt_config->endGroup();
+}
+
+void Config::ReadMotionTouchValues() {
+ int num_touch_from_button_maps =
+ qt_config->beginReadArray(QStringLiteral("touch_from_button_maps"));
+
+ if (num_touch_from_button_maps > 0) {
+ const auto append_touch_from_button_map = [this] {
+ Settings::TouchFromButtonMap map;
+ map.name = ReadSetting(QStringLiteral("name"), QStringLiteral("default"))
+ .toString()
+ .toStdString();
+ const int num_touch_maps = qt_config->beginReadArray(QStringLiteral("entries"));
+ map.buttons.reserve(num_touch_maps);
+ for (int i = 0; i < num_touch_maps; i++) {
+ qt_config->setArrayIndex(i);
+ std::string touch_mapping =
+ ReadSetting(QStringLiteral("bind")).toString().toStdString();
+ map.buttons.emplace_back(std::move(touch_mapping));
+ }
+ qt_config->endArray(); // entries
+ Settings::values.touch_from_button_maps.emplace_back(std::move(map));
+ };
+
+ for (int i = 0; i < num_touch_from_button_maps; ++i) {
+ qt_config->setArrayIndex(i);
+ append_touch_from_button_map();
+ }
+ } else {
+ Settings::values.touch_from_button_maps.emplace_back(
+ Settings::TouchFromButtonMap{"default", {}});
+ num_touch_from_button_maps = 1;
+ }
+ qt_config->endArray();
Settings::values.motion_device =
ReadSetting(QStringLiteral("motion_device"),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"))
.toString()
.toStdString();
+ Settings::values.touch_device =
+ ReadSetting(QStringLiteral("touch_device"), QStringLiteral("engine:emu_window"))
+ .toString()
+ .toStdString();
+ Settings::values.use_touch_from_button =
+ ReadSetting(QStringLiteral("use_touch_from_button"), false).toBool();
+ Settings::values.touch_from_button_map_index =
+ ReadSetting(QStringLiteral("touch_from_button_map"), 0).toInt();
+ Settings::values.touch_from_button_map_index =
+ std::clamp(Settings::values.touch_from_button_map_index, 0, num_touch_from_button_maps - 1);
Settings::values.udp_input_address =
ReadSetting(QStringLiteral("udp_input_address"),
QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR))
@@ -448,16 +578,12 @@ void Config::ReadControlValues() {
.toInt());
Settings::values.udp_pad_index =
static_cast<u8>(ReadSetting(QStringLiteral("udp_pad_index"), 0).toUInt());
- Settings::values.use_docked_mode =
- ReadSetting(QStringLiteral("use_docked_mode"), false).toBool();
-
- qt_config->endGroup();
}
void Config::ReadCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
- ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), false);
+ ReadSettingGlobal(Settings::values.use_multi_core, QStringLiteral("use_multi_core"), true);
qt_config->endGroup();
}
@@ -501,7 +627,7 @@ void Config::ReadDataStorageValues() {
Settings::values.gamecard_current_game =
ReadSetting(QStringLiteral("gamecard_current_game"), false).toBool();
Settings::values.gamecard_path =
- ReadSetting(QStringLiteral("gamecard_path"), QStringLiteral("")).toString().toStdString();
+ ReadSetting(QStringLiteral("gamecard_path"), QString{}).toString().toStdString();
qt_config->endGroup();
}
@@ -515,7 +641,7 @@ void Config::ReadDebuggingValues() {
Settings::values.use_gdbstub = ReadSetting(QStringLiteral("use_gdbstub"), false).toBool();
Settings::values.gdbstub_port = ReadSetting(QStringLiteral("gdbstub_port"), 24689).toInt();
Settings::values.program_args =
- ReadSetting(QStringLiteral("program_args"), QStringLiteral("")).toString().toStdString();
+ ReadSetting(QStringLiteral("program_args"), QString{}).toString().toStdString();
Settings::values.dump_exefs = ReadSetting(QStringLiteral("dump_exefs"), false).toBool();
Settings::values.dump_nso = ReadSetting(QStringLiteral("dump_nso"), false).toBool();
Settings::values.reporting_services =
@@ -550,8 +676,7 @@ void Config::ReadDisabledAddOnValues() {
const auto d_size = qt_config->beginReadArray(QStringLiteral("disabled"));
for (int j = 0; j < d_size; ++j) {
qt_config->setArrayIndex(j);
- out.push_back(
- ReadSetting(QStringLiteral("d"), QStringLiteral("")).toString().toStdString());
+ out.push_back(ReadSetting(QStringLiteral("d"), QString{}).toString().toStdString());
}
qt_config->endArray();
Settings::values.disabled_addons.insert_or_assign(title_id, out);
@@ -661,10 +786,12 @@ void Config::ReadRendererValues() {
QStringLiteral("use_disk_shader_cache"), true);
ReadSettingGlobal(Settings::values.gpu_accuracy, QStringLiteral("gpu_accuracy"), 0);
ReadSettingGlobal(Settings::values.use_asynchronous_gpu_emulation,
- QStringLiteral("use_asynchronous_gpu_emulation"), false);
+ QStringLiteral("use_asynchronous_gpu_emulation"), true);
+ ReadSettingGlobal(Settings::values.use_nvdec_emulation, QStringLiteral("use_nvdec_emulation"),
+ true);
ReadSettingGlobal(Settings::values.use_vsync, QStringLiteral("use_vsync"), true);
ReadSettingGlobal(Settings::values.use_assembly_shaders, QStringLiteral("use_assembly_shaders"),
- false);
+ true);
ReadSettingGlobal(Settings::values.use_asynchronous_shaders,
QStringLiteral("use_asynchronous_shaders"), false);
ReadSettingGlobal(Settings::values.use_fast_gpu_time, QStringLiteral("use_fast_gpu_time"),
@@ -790,14 +917,11 @@ void Config::ReadUIValues() {
UISettings::values.first_start = ReadSetting(QStringLiteral("firstStart"), true).toBool();
UISettings::values.callout_flags = ReadSetting(QStringLiteral("calloutFlags"), 0).toUInt();
UISettings::values.show_console = ReadSetting(QStringLiteral("showConsole"), false).toBool();
- UISettings::values.profile_index = ReadSetting(QStringLiteral("profileIndex"), 0).toUInt();
UISettings::values.pause_when_in_background =
ReadSetting(QStringLiteral("pauseWhenInBackground"), false).toBool();
UISettings::values.hide_mouse =
ReadSetting(QStringLiteral("hideInactiveMouse"), false).toBool();
- ApplyDefaultProfileIfInputInvalid();
-
qt_config->endGroup();
}
@@ -866,40 +990,64 @@ void Config::ReadValues() {
ReadSystemValues();
}
-void Config::SavePlayerValues() {
- for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
- const auto& player = Settings::values.players[p];
+void Config::SavePlayerValue(std::size_t player_index) {
+ const QString player_prefix = [this, player_index] {
+ if (type == ConfigType::InputProfile) {
+ return QString{};
+ } else {
+ return QStringLiteral("player_%1_").arg(player_index);
+ }
+ }();
- WriteSetting(QStringLiteral("player_%1_connected").arg(p), player.connected, false);
- WriteSetting(QStringLiteral("player_%1_type").arg(p), static_cast<u8>(player.type),
- static_cast<u8>(Settings::ControllerType::DualJoycon));
+ const auto& player = Settings::values.players.GetValue()[player_index];
- WriteSetting(QStringLiteral("player_%1_body_color_left").arg(p), player.body_color_left,
+ WriteSetting(QStringLiteral("%1type").arg(player_prefix),
+ static_cast<u8>(player.controller_type),
+ static_cast<u8>(Settings::ControllerType::ProController));
+
+ if (!player_prefix.isEmpty()) {
+ WriteSetting(QStringLiteral("%1connected").arg(player_prefix), player.connected, false);
+ WriteSetting(QStringLiteral("%1vibration_enabled").arg(player_prefix),
+ player.vibration_enabled, true);
+ WriteSetting(QStringLiteral("%1vibration_strength").arg(player_prefix),
+ player.vibration_strength, 100);
+ WriteSetting(QStringLiteral("%1body_color_left").arg(player_prefix), player.body_color_left,
Settings::JOYCON_BODY_NEON_BLUE);
- WriteSetting(QStringLiteral("player_%1_body_color_right").arg(p), player.body_color_right,
- Settings::JOYCON_BODY_NEON_RED);
- WriteSetting(QStringLiteral("player_%1_button_color_left").arg(p), player.button_color_left,
- Settings::JOYCON_BUTTONS_NEON_BLUE);
- WriteSetting(QStringLiteral("player_%1_button_color_right").arg(p),
+ WriteSetting(QStringLiteral("%1body_color_right").arg(player_prefix),
+ player.body_color_right, Settings::JOYCON_BODY_NEON_RED);
+ WriteSetting(QStringLiteral("%1button_color_left").arg(player_prefix),
+ player.button_color_left, Settings::JOYCON_BUTTONS_NEON_BLUE);
+ WriteSetting(QStringLiteral("%1button_color_right").arg(player_prefix),
player.button_color_right, Settings::JOYCON_BUTTONS_NEON_RED);
+ }
- for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
- const std::string default_param =
- InputCommon::GenerateKeyboardParam(default_buttons[i]);
- WriteSetting(QStringLiteral("player_%1_").arg(p) +
- QString::fromStdString(Settings::NativeButton::mapping[i]),
- QString::fromStdString(player.buttons[i]),
- QString::fromStdString(default_param));
- }
- for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
- const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
- default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
- WriteSetting(QStringLiteral("player_%1_").arg(p) +
- QString::fromStdString(Settings::NativeAnalog::mapping[i]),
- QString::fromStdString(player.analogs[i]),
- QString::fromStdString(default_param));
- }
+ for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeButton::mapping[i]),
+ QString::fromStdString(player.buttons[i]),
+ QString::fromStdString(default_param));
+ }
+ for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
+ const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
+ default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeAnalog::mapping[i]),
+ QString::fromStdString(player.analogs[i]),
+ QString::fromStdString(default_param));
+ }
+ for (int i = 0; i < Settings::NativeVibration::NumVibrations; ++i) {
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeVibration::mapping[i]),
+ QString::fromStdString(player.vibrations[i]), QString{});
+ }
+ for (int i = 0; i < Settings::NativeMotion::NumMotions; ++i) {
+ const std::string default_param = InputCommon::GenerateKeyboardParam(default_motions[i]);
+ WriteSetting(QStringLiteral("%1").arg(player_prefix) +
+ QString::fromStdString(Settings::NativeMotion::mapping[i]),
+ QString::fromStdString(player.motions[i]),
+ QString::fromStdString(default_param));
}
}
@@ -915,7 +1063,7 @@ void Config::SaveDebugValues() {
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
const std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
- default_analogs[i][3], default_analogs[i][4], 0.5f);
+ default_analogs[i][3], default_stick_mod[i], 0.5f);
WriteSetting(QStringLiteral("debug_pad_") +
QString::fromStdString(Settings::NativeAnalog::mapping[i]),
QString::fromStdString(Settings::values.debug_pad_analogs[i]),
@@ -949,6 +1097,43 @@ void Config::SaveTouchscreenValues() {
WriteSetting(QStringLiteral("touchscreen_diameter_y"), touchscreen.diameter_y, 15);
}
+void Config::SaveMotionTouchValues() {
+ WriteSetting(QStringLiteral("motion_device"),
+ QString::fromStdString(Settings::values.motion_device),
+ QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
+ WriteSetting(QStringLiteral("touch_device"),
+ QString::fromStdString(Settings::values.touch_device),
+ QStringLiteral("engine:emu_window"));
+ WriteSetting(QStringLiteral("use_touch_from_button"), Settings::values.use_touch_from_button,
+ false);
+ WriteSetting(QStringLiteral("touch_from_button_map"),
+ Settings::values.touch_from_button_map_index, 0);
+ WriteSetting(QStringLiteral("udp_input_address"),
+ QString::fromStdString(Settings::values.udp_input_address),
+ QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR));
+ WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
+ InputCommon::CemuhookUDP::DEFAULT_PORT);
+ WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
+
+ qt_config->beginWriteArray(QStringLiteral("touch_from_button_maps"));
+ for (std::size_t p = 0; p < Settings::values.touch_from_button_maps.size(); ++p) {
+ qt_config->setArrayIndex(static_cast<int>(p));
+ WriteSetting(QStringLiteral("name"),
+ QString::fromStdString(Settings::values.touch_from_button_maps[p].name),
+ QStringLiteral("default"));
+ qt_config->beginWriteArray(QStringLiteral("entries"));
+ for (std::size_t q = 0; q < Settings::values.touch_from_button_maps[p].buttons.size();
+ ++q) {
+ qt_config->setArrayIndex(static_cast<int>(q));
+ WriteSetting(
+ QStringLiteral("bind"),
+ QString::fromStdString(Settings::values.touch_from_button_maps[p].buttons[q]));
+ }
+ qt_config->endArray();
+ }
+ qt_config->endArray();
+}
+
void Config::SaveValues() {
if (global) {
SaveControlValues();
@@ -987,22 +1172,27 @@ void Config::SaveAudioValues() {
void Config::SaveControlValues() {
qt_config->beginGroup(QStringLiteral("Controls"));
- SavePlayerValues();
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
+ SavePlayerValue(p);
+ }
SaveDebugValues();
SaveMouseValues();
SaveTouchscreenValues();
+ SaveMotionTouchValues();
+ WriteSettingGlobal(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
+ WriteSettingGlobal(QStringLiteral("vibration_enabled"), Settings::values.vibration_enabled,
+ true);
+ WriteSettingGlobal(QStringLiteral("enable_accurate_vibrations"),
+ Settings::values.enable_accurate_vibrations, false);
+ WriteSettingGlobal(QStringLiteral("motion_enabled"), Settings::values.motion_enabled, true);
WriteSetting(QStringLiteral("motion_device"),
QString::fromStdString(Settings::values.motion_device),
QStringLiteral("engine:motion_emu,update_period:100,sensitivity:0.01"));
+ WriteSetting(QStringLiteral("touch_device"),
+ QString::fromStdString(Settings::values.touch_device),
+ QStringLiteral("engine:emu_window"));
WriteSetting(QStringLiteral("keyboard_enabled"), Settings::values.keyboard_enabled, false);
- WriteSetting(QStringLiteral("udp_input_address"),
- QString::fromStdString(Settings::values.udp_input_address),
- QString::fromUtf8(InputCommon::CemuhookUDP::DEFAULT_ADDR));
- WriteSetting(QStringLiteral("udp_input_port"), Settings::values.udp_input_port,
- InputCommon::CemuhookUDP::DEFAULT_PORT);
- WriteSetting(QStringLiteral("udp_pad_index"), Settings::values.udp_pad_index, 0);
- WriteSetting(QStringLiteral("use_docked_mode"), Settings::values.use_docked_mode, false);
qt_config->endGroup();
}
@@ -1010,7 +1200,7 @@ void Config::SaveControlValues() {
void Config::SaveCoreValues() {
qt_config->beginGroup(QStringLiteral("Core"));
- WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, false);
+ WriteSettingGlobal(QStringLiteral("use_multi_core"), Settings::values.use_multi_core, true);
qt_config->endGroup();
}
@@ -1038,7 +1228,7 @@ void Config::SaveDataStorageValues() {
WriteSetting(QStringLiteral("gamecard_current_game"), Settings::values.gamecard_current_game,
false);
WriteSetting(QStringLiteral("gamecard_path"),
- QString::fromStdString(Settings::values.gamecard_path), QStringLiteral(""));
+ QString::fromStdString(Settings::values.gamecard_path), QString{});
qt_config->endGroup();
}
@@ -1051,7 +1241,7 @@ void Config::SaveDebuggingValues() {
WriteSetting(QStringLiteral("use_gdbstub"), Settings::values.use_gdbstub, false);
WriteSetting(QStringLiteral("gdbstub_port"), Settings::values.gdbstub_port, 24689);
WriteSetting(QStringLiteral("program_args"),
- QString::fromStdString(Settings::values.program_args), QStringLiteral(""));
+ QString::fromStdString(Settings::values.program_args), QString{});
WriteSetting(QStringLiteral("dump_exefs"), Settings::values.dump_exefs, false);
WriteSetting(QStringLiteral("dump_nso"), Settings::values.dump_nso, false);
WriteSetting(QStringLiteral("quest_flag"), Settings::values.quest_flag, false);
@@ -1078,8 +1268,7 @@ void Config::SaveDisabledAddOnValues() {
qt_config->beginWriteArray(QStringLiteral("disabled"));
for (std::size_t j = 0; j < elem.second.size(); ++j) {
qt_config->setArrayIndex(static_cast<int>(j));
- WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]),
- QStringLiteral(""));
+ WriteSetting(QStringLiteral("d"), QString::fromStdString(elem.second[j]), QString{});
}
qt_config->endArray();
++i;
@@ -1167,10 +1356,12 @@ void Config::SaveRendererValues() {
static_cast<int>(Settings::values.gpu_accuracy.GetValue(global)),
Settings::values.gpu_accuracy.UsingGlobal(), 0);
WriteSettingGlobal(QStringLiteral("use_asynchronous_gpu_emulation"),
- Settings::values.use_asynchronous_gpu_emulation, false);
+ Settings::values.use_asynchronous_gpu_emulation, true);
+ WriteSettingGlobal(QStringLiteral("use_nvdec_emulation"), Settings::values.use_nvdec_emulation,
+ true);
WriteSettingGlobal(QStringLiteral("use_vsync"), Settings::values.use_vsync, true);
WriteSettingGlobal(QStringLiteral("use_assembly_shaders"),
- Settings::values.use_assembly_shaders, false);
+ Settings::values.use_assembly_shaders, true);
WriteSettingGlobal(QStringLiteral("use_asynchronous_shaders"),
Settings::values.use_asynchronous_shaders, false);
WriteSettingGlobal(QStringLiteral("use_fast_gpu_time"), Settings::values.use_fast_gpu_time,
@@ -1268,7 +1459,6 @@ void Config::SaveUIValues() {
WriteSetting(QStringLiteral("firstStart"), UISettings::values.first_start, true);
WriteSetting(QStringLiteral("calloutFlags"), UISettings::values.callout_flags, 0);
WriteSetting(QStringLiteral("showConsole"), UISettings::values.show_console, false);
- WriteSetting(QStringLiteral("profileIndex"), UISettings::values.profile_index, 0);
WriteSetting(QStringLiteral("pauseWhenInBackground"),
UISettings::values.pause_when_in_background, false);
WriteSetting(QStringLiteral("hideInactiveMouse"), UISettings::values.hide_mouse, false);
@@ -1415,3 +1605,19 @@ void Config::Save() {
Settings::Sanitize();
SaveValues();
}
+
+void Config::ReadControlPlayerValue(std::size_t player_index) {
+ qt_config->beginGroup(QStringLiteral("Controls"));
+ ReadPlayerValue(player_index);
+ qt_config->endGroup();
+}
+
+void Config::SaveControlPlayerValue(std::size_t player_index) {
+ qt_config->beginGroup(QStringLiteral("Controls"));
+ SavePlayerValue(player_index);
+ qt_config->endGroup();
+}
+
+const std::string& Config::GetConfigFilePath() const {
+ return qt_config_loc;
+}
diff --git a/src/yuzu/configuration/config.h b/src/yuzu/configuration/config.h
index e5f39b040..8a600e19d 100644
--- a/src/yuzu/configuration/config.h
+++ b/src/yuzu/configuration/config.h
@@ -16,14 +16,28 @@ class QSettings;
class Config {
public:
- explicit Config(const std::string& config_loc = "qt-config.ini", bool is_global = true);
+ enum class ConfigType {
+ GlobalConfig,
+ PerGameConfig,
+ InputProfile,
+ };
+
+ explicit Config(const std::string& config_name = "qt-config",
+ ConfigType config_type = ConfigType::GlobalConfig);
~Config();
void Reload();
void Save();
+ void ReadControlPlayerValue(std::size_t player_index);
+ void SaveControlPlayerValue(std::size_t player_index);
+
+ const std::string& GetConfigFilePath() const;
+
static const std::array<int, Settings::NativeButton::NumButtons> default_buttons;
- static const std::array<std::array<int, 5>, Settings::NativeAnalog::NumAnalogs> default_analogs;
+ static const std::array<int, Settings::NativeMotion::NumMotions> default_motions;
+ static const std::array<std::array<int, 4>, Settings::NativeAnalog::NumAnalogs> default_analogs;
+ static const std::array<int, 2> default_stick_mod;
static const std::array<int, Settings::NativeMouseButton::NumMouseButtons>
default_mouse_buttons;
static const std::array<int, Settings::NativeKeyboard::NumKeyboardKeys> default_keyboard_keys;
@@ -31,13 +45,15 @@ public:
static const std::array<UISettings::Shortcut, 16> default_hotkeys;
private:
+ void Initialize(const std::string& config_name);
+
void ReadValues();
- void ReadPlayerValues();
+ void ReadPlayerValue(std::size_t player_index);
void ReadDebugValues();
void ReadKeyboardValues();
void ReadMouseValues();
void ReadTouchscreenValues();
- void ApplyDefaultProfileIfInputInvalid();
+ void ReadMotionTouchValues();
// Read functions bases off the respective config section names.
void ReadAudioValues();
@@ -60,10 +76,11 @@ private:
void ReadWebServiceValues();
void SaveValues();
- void SavePlayerValues();
+ void SavePlayerValue(std::size_t player_index);
void SaveDebugValues();
void SaveMouseValues();
void SaveTouchscreenValues();
+ void SaveMotionTouchValues();
// Save functions based off the respective config section names.
void SaveAudioValues();
@@ -108,9 +125,9 @@ private:
void WriteSettingGlobal(const QString& name, const QVariant& value, bool use_global,
const QVariant& default_value);
+ ConfigType type;
std::unique_ptr<QSettings> qt_config;
std::string qt_config_loc;
-
bool global;
};
diff --git a/src/yuzu/configuration/configure.ui b/src/yuzu/configuration/configure.ui
index 5f5d8e571..f92c3aff3 100644
--- a/src/yuzu/configuration/configure.ui
+++ b/src/yuzu/configuration/configure.ui
@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
- <width>382</width>
+ <width>650</width>
<height>650</height>
</rect>
</property>
@@ -26,13 +26,13 @@
<widget class="QListWidget" name="selectorList">
<property name="minimumSize">
<size>
- <width>150</width>
+ <width>120</width>
<height>0</height>
</size>
</property>
<property name="maximumSize">
<size>
- <width>150</width>
+ <width>120</width>
<height>16777215</height>
</size>
</property>
@@ -44,76 +44,121 @@
<number>0</number>
</property>
<widget class="ConfigureGeneral" name="generalTab">
+ <property name="accessibleName">
+ <string>General</string>
+ </property>
<attribute name="title">
<string>General</string>
</attribute>
</widget>
<widget class="ConfigureUi" name="uiTab">
+ <property name="accessibleName">
+ <string>UI</string>
+ </property>
<attribute name="title">
<string>Game List</string>
</attribute>
</widget>
<widget class="ConfigureSystem" name="systemTab">
+ <property name="accessibleName">
+ <string>System</string>
+ </property>
<attribute name="title">
<string>System</string>
</attribute>
</widget>
<widget class="ConfigureProfileManager" name="profileManagerTab">
+ <property name="accessibleName">
+ <string>Profiles</string>
+ </property>
<attribute name="title">
<string>Profiles</string>
</attribute>
</widget>
<widget class="ConfigureFilesystem" name="filesystemTab">
+ <property name="accessibleName">
+ <string>Filesystem</string>
+ </property>
<attribute name="title">
<string>Filesystem</string>
</attribute>
</widget>
- <widget class="ConfigureInputSimple" name="inputTab">
+ <widget class="ConfigureInput" name="inputTab">
+ <property name="accessibleName">
+ <string>Controls</string>
+ </property>
<attribute name="title">
- <string>Input</string>
+ <string>Controls</string>
</attribute>
</widget>
<widget class="ConfigureHotkeys" name="hotkeysTab">
+ <property name="accessibleName">
+ <string>Hotkeys</string>
+ </property>
<attribute name="title">
<string>Hotkeys</string>
</attribute>
</widget>
<widget class="ConfigureCpu" name="cpuTab">
+ <property name="accessibleName">
+ <string>CPU</string>
+ </property>
<attribute name="title">
<string>CPU</string>
</attribute>
</widget>
<widget class="ConfigureCpuDebug" name="cpuDebugTab">
+ <property name="accessibleName">
+ <string>Debug</string>
+ </property>
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureGraphics" name="graphicsTab">
+ <property name="accessibleName">
+ <string>Graphics</string>
+ </property>
<attribute name="title">
<string>Graphics</string>
</attribute>
</widget>
<widget class="ConfigureGraphicsAdvanced" name="graphicsAdvancedTab">
+ <property name="accessibleName">
+ <string>Advanced</string>
+ </property>
<attribute name="title">
<string>GraphicsAdvanced</string>
</attribute>
</widget>
<widget class="ConfigureAudio" name="audioTab">
+ <property name="accessibleName">
+ <string>Audio</string>
+ </property>
<attribute name="title">
<string>Audio</string>
</attribute>
</widget>
<widget class="ConfigureDebug" name="debugTab">
+ <property name="accessibleName">
+ <string>Debug</string>
+ </property>
<attribute name="title">
<string>Debug</string>
</attribute>
</widget>
<widget class="ConfigureWeb" name="webTab">
+ <property name="accessibleName">
+ <string>Web</string>
+ </property>
<attribute name="title">
<string>Web</string>
</attribute>
</widget>
<widget class="ConfigureService" name="serviceTab">
+ <property name="accessibleName">
+ <string>Services</string>
+ </property>
<attribute name="title">
<string>Services</string>
</attribute>
@@ -205,9 +250,9 @@
<container>1</container>
</customwidget>
<customwidget>
- <class>ConfigureInputSimple</class>
+ <class>ConfigureInput</class>
<extends>QWidget</extends>
- <header>configuration/configure_input_simple.h</header>
+ <header>configuration/configure_input.h</header>
<container>1</container>
</customwidget>
<customwidget>
@@ -230,32 +275,12 @@
<signal>accepted()</signal>
<receiver>ConfigureDialog</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>220</x>
- <y>380</y>
- </hint>
- <hint type="destinationlabel">
- <x>220</x>
- <y>200</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureDialog</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>220</x>
- <y>380</y>
- </hint>
- <hint type="destinationlabel">
- <x>220</x>
- <y>200</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_audio.cpp b/src/yuzu/configuration/configure_audio.cpp
index fa9124ecf..db9518798 100644
--- a/src/yuzu/configuration/configure_audio.cpp
+++ b/src/yuzu/configuration/configure_audio.cpp
@@ -25,8 +25,8 @@ ConfigureAudio::ConfigureAudio(QWidget* parent)
connect(ui->output_sink_combo_box, qOverload<int>(&QComboBox::currentIndexChanged), this,
&ConfigureAudio::UpdateAudioDevices);
- ui->volume_label->setVisible(Settings::configuring_global);
- ui->volume_combo_box->setVisible(!Settings::configuring_global);
+ ui->volume_label->setVisible(Settings::IsConfiguringGlobal());
+ ui->volume_combo_box->setVisible(!Settings::IsConfiguringGlobal());
SetupPerGameUI();
@@ -51,7 +51,7 @@ void ConfigureAudio::SetConfiguration() {
ui->toggle_audio_stretching->setChecked(Settings::values.enable_audio_stretching.GetValue());
- if (!Settings::configuring_global) {
+ if (!Settings::IsConfiguringGlobal()) {
if (Settings::values.volume.UsingGlobal()) {
ui->volume_combo_box->setCurrentIndex(0);
ui->volume_slider->setEnabled(false);
@@ -99,7 +99,7 @@ void ConfigureAudio::SetVolumeIndicatorText(int percentage) {
}
void ConfigureAudio::ApplyConfiguration() {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
Settings::values.sink_id =
ui->output_sink_combo_box->itemText(ui->output_sink_combo_box->currentIndex())
.toStdString();
@@ -165,7 +165,7 @@ void ConfigureAudio::RetranslateUI() {
}
void ConfigureAudio::SetupPerGameUI() {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->volume_slider->setEnabled(Settings::values.volume.UsingGlobal());
ui->toggle_audio_stretching->setEnabled(
Settings::values.enable_audio_stretching.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_debug_controller.cpp b/src/yuzu/configuration/configure_debug_controller.cpp
new file mode 100644
index 000000000..a878ef9c6
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.cpp
@@ -0,0 +1,43 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "ui_configure_debug_controller.h"
+#include "yuzu/configuration/configure_debug_controller.h"
+#include "yuzu/configuration/configure_input_player.h"
+
+ConfigureDebugController::ConfigureDebugController(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem,
+ InputProfiles* profiles)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureDebugController>()),
+ debug_controller(
+ new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, true)) {
+ ui->setupUi(this);
+
+ ui->controllerLayout->addWidget(debug_controller);
+
+ connect(ui->clear_all_button, &QPushButton::clicked, this,
+ [this] { debug_controller->ClearAll(); });
+ connect(ui->restore_defaults_button, &QPushButton::clicked, this,
+ [this] { debug_controller->RestoreDefaults(); });
+
+ RetranslateUI();
+}
+
+ConfigureDebugController::~ConfigureDebugController() = default;
+
+void ConfigureDebugController::ApplyConfiguration() {
+ debug_controller->ApplyConfiguration();
+}
+
+void ConfigureDebugController::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigureDebugController::RetranslateUI() {
+ ui->retranslateUi(this);
+}
diff --git a/src/yuzu/configuration/configure_debug_controller.h b/src/yuzu/configuration/configure_debug_controller.h
new file mode 100644
index 000000000..b4f53fad5
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.h
@@ -0,0 +1,41 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QDialog>
+
+class QPushButton;
+
+class ConfigureInputPlayer;
+
+class InputProfiles;
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
+namespace Ui {
+class ConfigureDebugController;
+}
+
+class ConfigureDebugController : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureDebugController(QWidget* parent, InputCommon::InputSubsystem* input_subsystem,
+ InputProfiles* profiles);
+ ~ConfigureDebugController() override;
+
+ void ApplyConfiguration();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ std::unique_ptr<Ui::ConfigureDebugController> ui;
+
+ ConfigureInputPlayer* debug_controller;
+};
diff --git a/src/yuzu/configuration/configure_debug_controller.ui b/src/yuzu/configuration/configure_debug_controller.ui
new file mode 100644
index 000000000..7b7e6582c
--- /dev/null
+++ b/src/yuzu/configuration/configure_debug_controller.ui
@@ -0,0 +1,77 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureDebugController</class>
+ <widget class="QDialog" name="ConfigureDebugController">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>780</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Debug Controller</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="controllerLayout"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="clear_all_button">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="restore_defaults_button">
+ <property name="text">
+ <string>Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureDebugController</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureDebugController</receiver>
+ <slot>reject()</slot>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/configuration/configure_dialog.cpp b/src/yuzu/configuration/configure_dialog.cpp
index 4e30dc51e..5041e0bf8 100644
--- a/src/yuzu/configuration/configure_dialog.cpp
+++ b/src/yuzu/configuration/configure_dialog.cpp
@@ -12,14 +12,17 @@
#include "yuzu/configuration/configure_input_player.h"
#include "yuzu/hotkeys.h"
-ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry)
+ConfigureDialog::ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
+ InputCommon::InputSubsystem* input_subsystem)
: QDialog(parent), ui(new Ui::ConfigureDialog), registry(registry) {
- Settings::configuring_global = true;
+ Settings::SetConfiguringGlobal(true);
ui->setupUi(this);
ui->hotkeysTab->Populate(registry);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
+ ui->inputTab->Initialize(input_subsystem);
+
SetConfiguration();
PopulateSelectionList();
@@ -80,12 +83,12 @@ Q_DECLARE_METATYPE(QList<QWidget*>);
void ConfigureDialog::PopulateSelectionList() {
const std::array<std::pair<QString, QList<QWidget*>>, 6> items{
- {{tr("General"), {ui->generalTab, ui->webTab, ui->debugTab, ui->uiTab}},
+ {{tr("General"), {ui->generalTab, ui->hotkeysTab, ui->uiTab, ui->webTab, ui->debugTab}},
{tr("System"), {ui->systemTab, ui->profileManagerTab, ui->serviceTab, ui->filesystemTab}},
{tr("CPU"), {ui->cpuTab, ui->cpuDebugTab}},
{tr("Graphics"), {ui->graphicsTab, ui->graphicsAdvancedTab}},
{tr("Audio"), {ui->audioTab}},
- {tr("Controls"), {ui->inputTab, ui->hotkeysTab}}},
+ {tr("Controls"), ui->inputTab->GetSubTabs()}},
};
[[maybe_unused]] const QSignalBlocker blocker(ui->selectorList);
@@ -117,7 +120,7 @@ void ConfigureDialog::UpdateVisibleTabs() {
{ui->generalTab, tr("General")},
{ui->systemTab, tr("System")},
{ui->profileManagerTab, tr("Profiles")},
- {ui->inputTab, tr("Input")},
+ {ui->inputTab, tr("Controls")},
{ui->hotkeysTab, tr("Hotkeys")},
{ui->cpuTab, tr("CPU")},
{ui->cpuDebugTab, tr("Debug")},
@@ -138,6 +141,6 @@ void ConfigureDialog::UpdateVisibleTabs() {
const QList<QWidget*> tabs = qvariant_cast<QList<QWidget*>>(items[0]->data(Qt::UserRole));
for (const auto tab : tabs) {
- ui->tabWidget->addTab(tab, widgets.at(tab));
+ ui->tabWidget->addTab(tab, tab->accessibleName());
}
}
diff --git a/src/yuzu/configuration/configure_dialog.h b/src/yuzu/configuration/configure_dialog.h
index 4289bc225..570c3b941 100644
--- a/src/yuzu/configuration/configure_dialog.h
+++ b/src/yuzu/configuration/configure_dialog.h
@@ -9,6 +9,10 @@
class HotkeyRegistry;
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace Ui {
class ConfigureDialog;
}
@@ -17,7 +21,8 @@ class ConfigureDialog : public QDialog {
Q_OBJECT
public:
- explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry);
+ explicit ConfigureDialog(QWidget* parent, HotkeyRegistry& registry,
+ InputCommon::InputSubsystem* input_subsystem);
~ConfigureDialog() override;
void ApplyConfiguration();
diff --git a/src/yuzu/configuration/configure_general.cpp b/src/yuzu/configuration/configure_general.cpp
index 830096ea0..d4d29d422 100644
--- a/src/yuzu/configuration/configure_general.cpp
+++ b/src/yuzu/configuration/configure_general.cpp
@@ -19,7 +19,7 @@ ConfigureGeneral::ConfigureGeneral(QWidget* parent)
SetConfiguration();
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
connect(ui->toggle_frame_limit, &QCheckBox::clicked, ui->frame_limit,
[this]() { ui->frame_limit->setEnabled(ui->toggle_frame_limit->isChecked()); });
}
@@ -41,7 +41,7 @@ void ConfigureGeneral::SetConfiguration() {
ui->toggle_frame_limit->setChecked(Settings::values.use_frame_limit.GetValue());
ui->frame_limit->setValue(Settings::values.frame_limit.GetValue());
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue());
} else {
ui->frame_limit->setEnabled(Settings::values.use_frame_limit.GetValue() &&
@@ -50,7 +50,7 @@ void ConfigureGeneral::SetConfiguration() {
}
void ConfigureGeneral::ApplyConfiguration() {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
UISettings::values.confirm_before_closing = ui->toggle_check_exit->isChecked();
UISettings::values.select_user_on_boot = ui->toggle_user_on_boot->isChecked();
UISettings::values.pause_when_in_background = ui->toggle_background_pause->isChecked();
@@ -93,7 +93,7 @@ void ConfigureGeneral::RetranslateUI() {
}
void ConfigureGeneral::SetupPerGameUI() {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->toggle_frame_limit->setEnabled(Settings::values.use_frame_limit.UsingGlobal());
ui->frame_limit->setEnabled(Settings::values.frame_limit.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_graphics.cpp b/src/yuzu/configuration/configure_graphics.cpp
index 07d818548..6fda0ce35 100644
--- a/src/yuzu/configuration/configure_graphics.cpp
+++ b/src/yuzu/configuration/configure_graphics.cpp
@@ -33,7 +33,7 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
connect(ui->api, qOverload<int>(&QComboBox::currentIndexChanged), this, [this] {
UpdateDeviceComboBox();
- if (!Settings::configuring_global) {
+ if (!Settings::IsConfiguringGlobal()) {
ConfigurationShared::SetHighlight(
ui->api_layout, ui->api->currentIndex() != ConfigurationShared::USE_GLOBAL_INDEX);
}
@@ -49,8 +49,8 @@ ConfigureGraphics::ConfigureGraphics(QWidget* parent)
UpdateBackgroundColorButton(new_bg_color);
});
- ui->bg_label->setVisible(Settings::configuring_global);
- ui->bg_combobox->setVisible(!Settings::configuring_global);
+ ui->bg_label->setVisible(Settings::IsConfiguringGlobal());
+ ui->bg_combobox->setVisible(!Settings::IsConfiguringGlobal());
}
void ConfigureGraphics::UpdateDeviceSelection(int device) {
@@ -70,11 +70,13 @@ void ConfigureGraphics::SetConfiguration() {
ui->api->setEnabled(runtime_lock);
ui->use_asynchronous_gpu_emulation->setEnabled(runtime_lock);
ui->use_disk_shader_cache->setEnabled(runtime_lock);
+ ui->use_nvdec_emulation->setEnabled(runtime_lock);
ui->use_disk_shader_cache->setChecked(Settings::values.use_disk_shader_cache.GetValue());
ui->use_asynchronous_gpu_emulation->setChecked(
Settings::values.use_asynchronous_gpu_emulation.GetValue());
+ ui->use_nvdec_emulation->setChecked(Settings::values.use_nvdec_emulation.GetValue());
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->api->setCurrentIndex(static_cast<int>(Settings::values.renderer_backend.GetValue()));
ui->aspect_ratio_combobox->setCurrentIndex(Settings::values.aspect_ratio.GetValue());
} else {
@@ -98,7 +100,7 @@ void ConfigureGraphics::SetConfiguration() {
}
void ConfigureGraphics::ApplyConfiguration() {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
// Guard if during game and set to game-specific value
if (Settings::values.renderer_backend.UsingGlobal()) {
Settings::values.renderer_backend.SetValue(GetCurrentGraphicsBackend());
@@ -116,6 +118,9 @@ void ConfigureGraphics::ApplyConfiguration() {
Settings::values.use_asynchronous_gpu_emulation.SetValue(
ui->use_asynchronous_gpu_emulation->isChecked());
}
+ if (Settings::values.use_nvdec_emulation.UsingGlobal()) {
+ Settings::values.use_nvdec_emulation.SetValue(ui->use_nvdec_emulation->isChecked());
+ }
if (Settings::values.bg_red.UsingGlobal()) {
Settings::values.bg_red.SetValue(static_cast<float>(bg_color.redF()));
Settings::values.bg_green.SetValue(static_cast<float>(bg_color.greenF()));
@@ -144,6 +149,8 @@ void ConfigureGraphics::ApplyConfiguration() {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_asynchronous_gpu_emulation,
ui->use_asynchronous_gpu_emulation,
use_asynchronous_gpu_emulation);
+ ConfigurationShared::ApplyPerGameSetting(&Settings::values.use_nvdec_emulation,
+ ui->use_nvdec_emulation, use_nvdec_emulation);
if (ui->bg_combobox->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
Settings::values.bg_red.SetGlobal(true);
@@ -187,7 +194,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
bool enabled = false;
- if (!Settings::configuring_global &&
+ if (!Settings::IsConfiguringGlobal() &&
ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX) {
vulkan_device = Settings::values.vulkan_device.GetValue();
}
@@ -205,7 +212,7 @@ void ConfigureGraphics::UpdateDeviceComboBox() {
break;
}
// If in per-game config and use global is selected, don't enable.
- enabled &= !(!Settings::configuring_global &&
+ enabled &= !(!Settings::IsConfiguringGlobal() &&
ui->api->currentIndex() == ConfigurationShared::USE_GLOBAL_INDEX);
ui->device->setEnabled(enabled && !Core::System::GetInstance().IsPoweredOn());
}
@@ -220,7 +227,7 @@ void ConfigureGraphics::RetrieveVulkanDevices() {
}
Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
return static_cast<Settings::RendererBackend>(ui->api->currentIndex());
}
@@ -234,12 +241,13 @@ Settings::RendererBackend ConfigureGraphics::GetCurrentGraphicsBackend() const {
}
void ConfigureGraphics::SetupPerGameUI() {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->api->setEnabled(Settings::values.renderer_backend.UsingGlobal());
ui->device->setEnabled(Settings::values.renderer_backend.UsingGlobal());
ui->aspect_ratio_combobox->setEnabled(Settings::values.aspect_ratio.UsingGlobal());
ui->use_asynchronous_gpu_emulation->setEnabled(
Settings::values.use_asynchronous_gpu_emulation.UsingGlobal());
+ ui->use_nvdec_emulation->setEnabled(Settings::values.use_nvdec_emulation.UsingGlobal());
ui->use_disk_shader_cache->setEnabled(Settings::values.use_disk_shader_cache.UsingGlobal());
ui->bg_button->setEnabled(Settings::values.bg_red.UsingGlobal());
@@ -253,6 +261,8 @@ void ConfigureGraphics::SetupPerGameUI() {
ConfigurationShared::SetColoredTristate(
ui->use_disk_shader_cache, Settings::values.use_disk_shader_cache, use_disk_shader_cache);
+ ConfigurationShared::SetColoredTristate(
+ ui->use_nvdec_emulation, Settings::values.use_nvdec_emulation, use_nvdec_emulation);
ConfigurationShared::SetColoredTristate(ui->use_asynchronous_gpu_emulation,
Settings::values.use_asynchronous_gpu_emulation,
use_asynchronous_gpu_emulation);
diff --git a/src/yuzu/configuration/configure_graphics.h b/src/yuzu/configuration/configure_graphics.h
index b4961f719..1fefc88eb 100644
--- a/src/yuzu/configuration/configure_graphics.h
+++ b/src/yuzu/configuration/configure_graphics.h
@@ -46,6 +46,7 @@ private:
std::unique_ptr<Ui::ConfigureGraphics> ui;
QColor bg_color;
+ ConfigurationShared::CheckState use_nvdec_emulation;
ConfigurationShared::CheckState use_disk_shader_cache;
ConfigurationShared::CheckState use_asynchronous_gpu_emulation;
diff --git a/src/yuzu/configuration/configure_graphics.ui b/src/yuzu/configuration/configure_graphics.ui
index 62aa337e7..58486eb1e 100644
--- a/src/yuzu/configuration/configure_graphics.ui
+++ b/src/yuzu/configuration/configure_graphics.ui
@@ -98,6 +98,13 @@
</widget>
</item>
<item>
+ <widget class="QCheckBox" name="use_nvdec_emulation">
+ <property name="text">
+ <string>Use NVDEC emulation</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="QWidget" name="aspect_ratio_layout" native="true">
<layout class="QHBoxLayout" name="horizontalLayout_6">
<property name="leftMargin">
diff --git a/src/yuzu/configuration/configure_graphics_advanced.cpp b/src/yuzu/configuration/configure_graphics_advanced.cpp
index 73f276949..383c7bac8 100644
--- a/src/yuzu/configuration/configure_graphics_advanced.cpp
+++ b/src/yuzu/configuration/configure_graphics_advanced.cpp
@@ -32,7 +32,7 @@ void ConfigureGraphicsAdvanced::SetConfiguration() {
ui->use_asynchronous_shaders->setChecked(Settings::values.use_asynchronous_shaders.GetValue());
ui->use_fast_gpu_time->setChecked(Settings::values.use_fast_gpu_time.GetValue());
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->gpu_accuracy->setCurrentIndex(
static_cast<int>(Settings::values.gpu_accuracy.GetValue()));
ui->anisotropic_filtering_combobox->setCurrentIndex(
@@ -52,9 +52,9 @@ void ConfigureGraphicsAdvanced::ApplyConfiguration() {
// Subtract 2 if configuring per-game (separator and "use global configuration" take 2 slots)
const auto gpu_accuracy = static_cast<Settings::GPUAccuracy>(
ui->gpu_accuracy->currentIndex() -
- ((Settings::configuring_global) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
+ ((Settings::IsConfiguringGlobal()) ? 0 : ConfigurationShared::USE_GLOBAL_OFFSET));
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
// Must guard in case of a during-game configuration when set to be game-specific.
if (Settings::values.gpu_accuracy.UsingGlobal()) {
Settings::values.gpu_accuracy.SetValue(gpu_accuracy);
@@ -118,7 +118,7 @@ void ConfigureGraphicsAdvanced::RetranslateUI() {
void ConfigureGraphicsAdvanced::SetupPerGameUI() {
// Disable if not global (only happens during game)
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->gpu_accuracy->setEnabled(Settings::values.gpu_accuracy.UsingGlobal());
ui->use_vsync->setEnabled(Settings::values.use_vsync.UsingGlobal());
ui->use_assembly_shaders->setEnabled(Settings::values.use_assembly_shaders.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_input.cpp b/src/yuzu/configuration/configure_input.cpp
index f2977719c..d9009091b 100644
--- a/src/yuzu/configuration/configure_input.cpp
+++ b/src/yuzu/configuration/configure_input.cpp
@@ -8,18 +8,35 @@
#include <QSignalBlocker>
#include <QTimer>
-#include "configuration/configure_touchscreen_advanced.h"
#include "core/core.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
-#include "core/hle/service/hid/controllers/npad.h"
#include "core/hle/service/sm/sm.h"
#include "ui_configure_input.h"
+#include "ui_configure_input_advanced.h"
#include "ui_configure_input_player.h"
+#include "yuzu/configuration/configure_debug_controller.h"
#include "yuzu/configuration/configure_input.h"
+#include "yuzu/configuration/configure_input_advanced.h"
#include "yuzu/configuration/configure_input_player.h"
+#include "yuzu/configuration/configure_motion_touch.h"
#include "yuzu/configuration/configure_mouse_advanced.h"
+#include "yuzu/configuration/configure_touchscreen_advanced.h"
+#include "yuzu/configuration/configure_vibration.h"
+#include "yuzu/configuration/input_profiles.h"
+
+namespace {
+template <typename Dialog, typename... Args>
+void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
+ Dialog dialog(&parent, std::forward<Args>(args)...);
+
+ const auto res = dialog.exec();
+ if (res == QDialog::Accepted) {
+ dialog.ApplyConfiguration();
+ }
+}
+} // Anonymous namespace
void OnDockedModeChanged(bool last_state, bool new_state) {
if (last_state == new_state) {
@@ -48,97 +65,134 @@ void OnDockedModeChanged(bool last_state, bool new_state) {
}
}
-namespace {
-template <typename Dialog, typename... Args>
-void CallConfigureDialog(ConfigureInput& parent, Args&&... args) {
- parent.ApplyConfiguration();
- Dialog dialog(&parent, std::forward<Args>(args)...);
-
- const auto res = dialog.exec();
- if (res == QDialog::Accepted) {
- dialog.ApplyConfiguration();
- }
-}
-} // Anonymous namespace
-
ConfigureInput::ConfigureInput(QWidget* parent)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureInput>()) {
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInput>()),
+ profiles(std::make_unique<InputProfiles>()) {
ui->setupUi(this);
+}
+
+ConfigureInput::~ConfigureInput() = default;
- players_controller = {
- ui->player1_combobox, ui->player2_combobox, ui->player3_combobox, ui->player4_combobox,
- ui->player5_combobox, ui->player6_combobox, ui->player7_combobox, ui->player8_combobox,
+void ConfigureInput::Initialize(InputCommon::InputSubsystem* input_subsystem,
+ std::size_t max_players) {
+ player_controllers = {
+ new ConfigureInputPlayer(this, 0, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 1, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 2, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 3, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 4, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 5, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 6, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
+ new ConfigureInputPlayer(this, 7, ui->consoleInputSettings, input_subsystem,
+ profiles.get()),
};
- players_configure = {
- ui->player1_configure, ui->player2_configure, ui->player3_configure, ui->player4_configure,
- ui->player5_configure, ui->player6_configure, ui->player7_configure, ui->player8_configure,
+ player_tabs = {
+ ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4,
+ ui->tabPlayer5, ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8,
};
- RetranslateUI();
- LoadConfiguration();
- UpdateUIEnabled();
+ player_connected = {
+ ui->checkboxPlayer1Connected, ui->checkboxPlayer2Connected, ui->checkboxPlayer3Connected,
+ ui->checkboxPlayer4Connected, ui->checkboxPlayer5Connected, ui->checkboxPlayer6Connected,
+ ui->checkboxPlayer7Connected, ui->checkboxPlayer8Connected,
+ };
- connect(ui->restore_defaults_button, &QPushButton::clicked, this,
- &ConfigureInput::RestoreDefaults);
+ std::array<QLabel*, 8> player_connected_labels = {
+ ui->label, ui->label_3, ui->label_4, ui->label_5,
+ ui->label_6, ui->label_7, ui->label_8, ui->label_9,
+ };
- for (auto* enabled : players_controller) {
- connect(enabled, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
- &ConfigureInput::UpdateUIEnabled);
- }
- connect(ui->use_docked_mode, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->handheld_connected, &QCheckBox::stateChanged, this,
- &ConfigureInput::UpdateUIEnabled);
- connect(ui->mouse_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->keyboard_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->debug_enabled, &QCheckBox::stateChanged, this, &ConfigureInput::UpdateUIEnabled);
- connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
- &ConfigureInput::UpdateUIEnabled);
-
- for (std::size_t i = 0; i < players_configure.size(); ++i) {
- connect(players_configure[i], &QPushButton::clicked, this,
- [this, i] { CallConfigureDialog<ConfigureInputPlayer>(*this, i, false); });
+ for (std::size_t i = 0; i < player_tabs.size(); ++i) {
+ player_tabs[i]->setLayout(new QHBoxLayout(player_tabs[i]));
+ player_tabs[i]->layout()->addWidget(player_controllers[i]);
+ connect(player_controllers[i], &ConfigureInputPlayer::Connected, [&, i](bool is_connected) {
+ if (is_connected) {
+ for (std::size_t index = 0; index <= i; ++index) {
+ player_connected[index]->setChecked(is_connected);
+ }
+ } else {
+ for (std::size_t index = i; index < player_tabs.size(); ++index) {
+ player_connected[index]->setChecked(is_connected);
+ }
+ }
+ });
+ connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputDevices, this,
+ &ConfigureInput::UpdateAllInputDevices);
+ connect(player_controllers[i], &ConfigureInputPlayer::RefreshInputProfiles, this,
+ &ConfigureInput::UpdateAllInputProfiles, Qt::QueuedConnection);
+ connect(player_connected[i], &QCheckBox::stateChanged, [this, i](int state) {
+ player_controllers[i]->ConnectPlayer(state == Qt::Checked);
+ });
+
+ // Remove/hide all the elements that exceed max_players, if applicable.
+ if (i >= max_players) {
+ ui->tabWidget->removeTab(static_cast<int>(max_players));
+ player_connected[i]->hide();
+ player_connected_labels[i]->hide();
+ }
}
+ // Only the first player can choose handheld mode so connect the signal just to player 1
+ connect(player_controllers[0], &ConfigureInputPlayer::HandheldStateChanged,
+ [this](bool is_handheld) { UpdateDockedState(is_handheld); });
+
+ advanced = new ConfigureInputAdvanced(this);
+ ui->tabAdvanced->setLayout(new QHBoxLayout(ui->tabAdvanced));
+ ui->tabAdvanced->layout()->addWidget(advanced);
+ connect(advanced, &ConfigureInputAdvanced::CallDebugControllerDialog, [this, input_subsystem] {
+ CallConfigureDialog<ConfigureDebugController>(*this, input_subsystem, profiles.get());
+ });
+ connect(advanced, &ConfigureInputAdvanced::CallMouseConfigDialog, [this, input_subsystem] {
+ CallConfigureDialog<ConfigureMouseAdvanced>(*this, input_subsystem);
+ });
+ connect(advanced, &ConfigureInputAdvanced::CallTouchscreenConfigDialog,
+ [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
+ connect(advanced, &ConfigureInputAdvanced::CallMotionTouchConfigDialog,
+ [this, input_subsystem] {
+ CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
+ });
- connect(ui->handheld_configure, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 8, false); });
+ connect(ui->vibrationButton, &QPushButton::clicked,
+ [this] { CallConfigureDialog<ConfigureVibration>(*this); });
- connect(ui->debug_configure, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureInputPlayer>(*this, 9, true); });
+ connect(ui->motionButton, &QPushButton::clicked, [this, input_subsystem] {
+ CallConfigureDialog<ConfigureMotionTouch>(*this, input_subsystem);
+ });
- connect(ui->mouse_advanced, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureMouseAdvanced>(*this); });
+ connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
+ connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
- connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
- [this] { CallConfigureDialog<ConfigureTouchscreenAdvanced>(*this); });
+ RetranslateUI();
+ LoadConfiguration();
}
-ConfigureInput::~ConfigureInput() = default;
+QList<QWidget*> ConfigureInput::GetSubTabs() const {
+ return {
+ ui->tabPlayer1, ui->tabPlayer2, ui->tabPlayer3, ui->tabPlayer4, ui->tabPlayer5,
+ ui->tabPlayer6, ui->tabPlayer7, ui->tabPlayer8, ui->tabAdvanced,
+ };
+}
void ConfigureInput::ApplyConfiguration() {
- for (std::size_t i = 0; i < players_controller.size(); ++i) {
- const auto controller_type_index = players_controller[i]->currentIndex();
+ for (auto controller : player_controllers) {
+ controller->ApplyConfiguration();
+ }
- Settings::values.players[i].connected = controller_type_index != 0;
+ advanced->ApplyConfiguration();
- if (controller_type_index > 0) {
- Settings::values.players[i].type =
- static_cast<Settings::ControllerType>(controller_type_index - 1);
- } else {
- Settings::values.players[i].type = Settings::ControllerType::DualJoycon;
- }
- }
+ const bool pre_docked_mode = Settings::values.use_docked_mode.GetValue();
+ Settings::values.use_docked_mode.SetValue(ui->radioDocked->isChecked());
+ OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode.GetValue());
- const bool pre_docked_mode = Settings::values.use_docked_mode;
- Settings::values.use_docked_mode = ui->use_docked_mode->isChecked();
- OnDockedModeChanged(pre_docked_mode, Settings::values.use_docked_mode);
- Settings::values
- .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
- .connected = ui->handheld_connected->isChecked();
- Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
- Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
- Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
- Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
+ Settings::values.vibration_enabled.SetValue(ui->vibrationGroup->isChecked());
+ Settings::values.motion_enabled.SetValue(ui->motionGroup->isChecked());
}
void ConfigureInput::changeEvent(QEvent* event) {
@@ -146,94 +200,74 @@ void ConfigureInput::changeEvent(QEvent* event) {
RetranslateUI();
}
- QDialog::changeEvent(event);
+ QWidget::changeEvent(event);
}
void ConfigureInput::RetranslateUI() {
ui->retranslateUi(this);
- RetranslateControllerComboBoxes();
}
-void ConfigureInput::RetranslateControllerComboBoxes() {
- for (auto* controller_box : players_controller) {
- [[maybe_unused]] const QSignalBlocker blocker(controller_box);
-
- controller_box->clear();
- controller_box->addItems({tr("None"), tr("Pro Controller"), tr("Dual Joycons"),
- tr("Single Right Joycon"), tr("Single Left Joycon")});
- }
-
+void ConfigureInput::LoadConfiguration() {
LoadPlayerControllerIndices();
+ UpdateDockedState(Settings::values.players.GetValue()[8].connected);
+
+ ui->vibrationGroup->setChecked(Settings::values.vibration_enabled.GetValue());
+ ui->motionGroup->setChecked(Settings::values.motion_enabled.GetValue());
}
-void ConfigureInput::UpdateUIEnabled() {
- bool hit_disabled = false;
- for (auto* player : players_controller) {
- player->setDisabled(hit_disabled);
- if (hit_disabled) {
- player->setCurrentIndex(0);
- }
- if (!hit_disabled && player->currentIndex() == 0) {
- hit_disabled = true;
- }
+void ConfigureInput::LoadPlayerControllerIndices() {
+ for (std::size_t i = 0; i < player_connected.size(); ++i) {
+ const auto connected = Settings::values.players.GetValue()[i].connected ||
+ (i == 0 && Settings::values.players.GetValue()[8].connected);
+ player_connected[i]->setChecked(connected);
}
+}
- for (std::size_t i = 0; i < players_controller.size(); ++i) {
- players_configure[i]->setEnabled(players_controller[i]->currentIndex() != 0);
- }
+void ConfigureInput::ClearAll() {
+ // We don't have a good way to know what tab is active, but we can find out by getting the
+ // parent of the consoleInputSettings
+ auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
+ player_tab->ClearAll();
+}
- ui->handheld_connected->setChecked(ui->handheld_connected->isChecked() &&
- !ui->use_docked_mode->isChecked());
- ui->handheld_connected->setEnabled(!ui->use_docked_mode->isChecked());
- ui->handheld_configure->setEnabled(ui->handheld_connected->isChecked() &&
- !ui->use_docked_mode->isChecked());
- ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
- ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
- ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
+void ConfigureInput::RestoreDefaults() {
+ // We don't have a good way to know what tab is active, but we can find out by getting the
+ // parent of the consoleInputSettings
+ auto* player_tab = static_cast<ConfigureInputPlayer*>(ui->consoleInputSettings->parent());
+ player_tab->RestoreDefaults();
+
+ ui->radioDocked->setChecked(true);
+ ui->radioUndocked->setChecked(false);
+ ui->vibrationGroup->setChecked(true);
+ ui->motionGroup->setChecked(true);
}
-void ConfigureInput::LoadConfiguration() {
- std::stable_partition(
- Settings::values.players.begin(),
- Settings::values.players.begin() +
- Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD),
- [](const auto& player) { return player.connected; });
+void ConfigureInput::UpdateDockedState(bool is_handheld) {
+ // Disallow changing the console mode if the controller type is handheld.
+ ui->radioDocked->setEnabled(!is_handheld);
+ ui->radioUndocked->setEnabled(!is_handheld);
- LoadPlayerControllerIndices();
+ ui->radioDocked->setChecked(Settings::values.use_docked_mode.GetValue());
+ ui->radioUndocked->setChecked(!Settings::values.use_docked_mode.GetValue());
- ui->use_docked_mode->setChecked(Settings::values.use_docked_mode);
- ui->handheld_connected->setChecked(
- Settings::values
- .players[Service::HID::Controller_NPad::NPadIdToIndex(Service::HID::NPAD_HANDHELD)]
- .connected);
- ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
- ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
- ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
- ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
-
- UpdateUIEnabled();
+ // Also force into undocked mode if the controller type is handheld.
+ if (is_handheld) {
+ ui->radioUndocked->setChecked(true);
+ }
}
-void ConfigureInput::LoadPlayerControllerIndices() {
- for (std::size_t i = 0; i < players_controller.size(); ++i) {
- const auto connected = Settings::values.players[i].connected;
- players_controller[i]->setCurrentIndex(
- connected ? static_cast<u8>(Settings::values.players[i].type) + 1 : 0);
+void ConfigureInput::UpdateAllInputDevices() {
+ for (const auto& player : player_controllers) {
+ player->UpdateInputDeviceCombobox();
}
}
-void ConfigureInput::RestoreDefaults() {
- players_controller[0]->setCurrentIndex(2);
+void ConfigureInput::UpdateAllInputProfiles(std::size_t player_index) {
+ for (std::size_t i = 0; i < player_controllers.size(); ++i) {
+ if (i == player_index) {
+ continue;
+ }
- for (std::size_t i = 1; i < players_controller.size(); ++i) {
- players_controller[i]->setCurrentIndex(0);
+ player_controllers[i]->UpdateInputProfiles();
}
-
- ui->use_docked_mode->setCheckState(Qt::Unchecked);
- ui->handheld_connected->setCheckState(Qt::Unchecked);
- ui->mouse_enabled->setCheckState(Qt::Unchecked);
- ui->keyboard_enabled->setCheckState(Qt::Unchecked);
- ui->debug_enabled->setCheckState(Qt::Unchecked);
- ui->touchscreen_enabled->setCheckState(Qt::Checked);
- UpdateUIEnabled();
}
diff --git a/src/yuzu/configuration/configure_input.h b/src/yuzu/configuration/configure_input.h
index 2f70cb3ca..f4eb0d78b 100644
--- a/src/yuzu/configuration/configure_input.h
+++ b/src/yuzu/configuration/configure_input.h
@@ -7,37 +7,52 @@
#include <array>
#include <memory>
-#include <QDialog>
#include <QKeyEvent>
+#include <QList>
+#include <QWidget>
-#include "ui_configure_input.h"
-
-class QPushButton;
+class QCheckBox;
class QString;
class QTimer;
+class ConfigureInputAdvanced;
+class ConfigureInputPlayer;
+
+class InputProfiles;
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace Ui {
class ConfigureInput;
}
void OnDockedModeChanged(bool last_state, bool new_state);
-class ConfigureInput : public QDialog {
+class ConfigureInput : public QWidget {
Q_OBJECT
public:
explicit ConfigureInput(QWidget* parent = nullptr);
~ConfigureInput() override;
- /// Save all button configurations to settings file
+ /// Initializes the input dialog with the given input subsystem.
+ void Initialize(InputCommon::InputSubsystem* input_subsystem_, std::size_t max_players = 8);
+
+ /// Save all button configurations to settings file.
void ApplyConfiguration();
+ QList<QWidget*> GetSubTabs() const;
+
private:
void changeEvent(QEvent* event) override;
void RetranslateUI();
- void RetranslateControllerComboBoxes();
+ void ClearAll();
- void UpdateUIEnabled();
+ void UpdateDockedState(bool is_handheld);
+ void UpdateAllInputDevices();
+ void UpdateAllInputProfiles(std::size_t player_index);
/// Load configuration settings.
void LoadConfiguration();
@@ -48,6 +63,10 @@ private:
std::unique_ptr<Ui::ConfigureInput> ui;
- std::array<QComboBox*, 8> players_controller;
- std::array<QPushButton*, 8> players_configure;
+ std::unique_ptr<InputProfiles> profiles;
+
+ std::array<ConfigureInputPlayer*, 8> player_controllers;
+ std::array<QWidget*, 8> player_tabs;
+ std::array<QCheckBox*, 8> player_connected;
+ ConfigureInputAdvanced* advanced;
};
diff --git a/src/yuzu/configuration/configure_input.ui b/src/yuzu/configuration/configure_input.ui
index efffd8487..2707025e7 100644
--- a/src/yuzu/configuration/configure_input.ui
+++ b/src/yuzu/configuration/configure_input.ui
@@ -1,529 +1,548 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInput</class>
- <widget class="QDialog" name="ConfigureInput">
+ <widget class="QWidget" name="ConfigureInput">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>384</width>
- <height>576</height>
+ <width>680</width>
+ <height>540</height>
</rect>
</property>
<property name="windowTitle">
- <string>Custom Input Settings</string>
+ <string>ConfigureInput</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
<item>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QGroupBox" name="gridGroupBox_1">
- <property name="title">
- <string>Players</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="2">
- <widget class="QComboBox" name="player1_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="1" column="3">
- <widget class="QPushButton" name="player1_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Controller Type</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignCenter</set>
- </property>
- </widget>
- </item>
- <item row="2" column="2">
- <widget class="QComboBox" name="player2_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="3" column="2">
- <widget class="QComboBox" name="player3_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="4" column="2">
- <widget class="QComboBox" name="player4_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="5" column="2">
- <widget class="QComboBox" name="player5_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="6" column="2">
- <widget class="QComboBox" name="player6_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="7" column="2">
- <widget class="QComboBox" name="player7_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="8" column="2">
- <widget class="QComboBox" name="player8_combobox">
- <property name="minimumSize">
- <size>
- <width>110</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="player2_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="3" column="3">
- <widget class="QPushButton" name="player3_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="4" column="3">
- <widget class="QPushButton" name="player4_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="5" column="3">
- <widget class="QPushButton" name="player5_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="6" column="3">
- <widget class="QPushButton" name="player6_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="7" column="3">
- <widget class="QPushButton" name="player7_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="8" column="3">
- <widget class="QPushButton" name="player8_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="4">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="label_3">
- <property name="minimumSize">
- <size>
- <width>55</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Player 1</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QLabel" name="label_4">
- <property name="text">
- <string>Player 2</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QLabel" name="label_5">
- <property name="text">
- <string>Player 3</string>
- </property>
- </widget>
- </item>
- <item row="4" column="1">
- <widget class="QLabel" name="label_6">
- <property name="text">
- <string>Player 4</string>
- </property>
- </widget>
- </item>
- <item row="5" column="1">
- <widget class="QLabel" name="label_7">
- <property name="text">
- <string>Player 5</string>
- </property>
- </widget>
- </item>
- <item row="6" column="1">
- <widget class="QLabel" name="label_8">
- <property name="text">
- <string>Player 6</string>
- </property>
- </widget>
- </item>
- <item row="7" column="1">
- <widget class="QLabel" name="label_9">
- <property name="text">
- <string>Player 7</string>
- </property>
- </widget>
- </item>
- <item row="8" column="1">
- <widget class="QLabel" name="label_10">
- <property name="text">
- <string>Player 8</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="gridGroupBox_2">
- <property name="title">
- <string>Handheld</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="1" column="2">
- <spacer name="horizontalSpacer_5">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>72</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="4">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="3">
- <widget class="QPushButton" name="handheld_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QCheckBox" name="handheld_connected">
- <property name="text">
- <string>Joycons Docked</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QCheckBox" name="use_docked_mode">
- <property name="text">
- <string>Use Docked Mode</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <widget class="QGroupBox" name="gridGroupBox_3">
- <property name="title">
- <string>Other</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="1" column="1">
- <widget class="QCheckBox" name="keyboard_enabled">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>23</height>
- </size>
- </property>
- <property name="text">
- <string>Keyboard</string>
- </property>
- </widget>
- </item>
- <item row="2" column="1">
- <widget class="QCheckBox" name="debug_enabled">
- <property name="text">
- <string>Debug Controller</string>
- </property>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QCheckBox" name="touchscreen_enabled">
- <property name="text">
- <string>Touchscreen</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QCheckBox" name="mouse_enabled">
- <property name="minimumSize">
- <size>
- <width>0</width>
- <height>23</height>
- </size>
- </property>
- <property name="text">
- <string>Mouse</string>
- </property>
- </widget>
- </item>
- <item row="0" column="4">
- <spacer name="horizontalSpacer_7">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="2">
- <spacer name="horizontalSpacer_8">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>76</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="0">
- <spacer name="horizontalSpacer_6">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="3" column="3">
- <widget class="QPushButton" name="touchscreen_advanced">
- <property name="text">
- <string>Advanced</string>
- </property>
- </widget>
- </item>
- <item row="2" column="3">
- <widget class="QPushButton" name="debug_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <widget class="QPushButton" name="mouse_advanced">
- <property name="text">
- <string>Advanced</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout">
- <item>
- <widget class="QPushButton" name="restore_defaults_button">
- <property name="text">
- <string>Restore Defaults</string>
+ <widget class="QTabWidget" name="tabWidget">
+ <property name="currentIndex">
+ <number>0</number>
+ </property>
+ <widget class="QWidget" name="tabPlayer1">
+ <property name="accessibleName">
+ <string>Player 1</string>
+ </property>
+ <attribute name="title">
+ <string>Player 1</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer2">
+ <property name="accessibleName">
+ <string>Player 2</string>
+ </property>
+ <attribute name="title">
+ <string>Player 2</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer3">
+ <property name="accessibleName">
+ <string>Player 3</string>
+ </property>
+ <attribute name="title">
+ <string>Player 3</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer4">
+ <property name="accessibleName">
+ <string>Player 4</string>
+ </property>
+ <attribute name="title">
+ <string>Player 4</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer5">
+ <property name="accessibleName">
+ <string>Player 5</string>
+ </property>
+ <attribute name="title">
+ <string>Player 5</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer6">
+ <property name="accessibleName">
+ <string>Player 6</string>
+ </property>
+ <attribute name="title">
+ <string>Player 6</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer7">
+ <property name="accessibleName">
+ <string>Player 7</string>
+ </property>
+ <attribute name="title">
+ <string>Player 7</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabPlayer8">
+ <property name="accessibleName">
+ <string>Player 8</string>
+ </property>
+ <attribute name="title">
+ <string>Player 8</string>
+ </attribute>
+ </widget>
+ <widget class="QWidget" name="tabAdvanced">
+ <property name="accessibleName">
+ <string>Advanced</string>
+ </property>
+ <attribute name="title">
+ <string>Advanced</string>
+ </attribute>
+ </widget>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignVCenter">
+ <widget class="QWidget" name="consoleInputSettings" native="true">
+ <layout class="QHBoxLayout" name="buttonsBottomRightHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignVCenter">
+ <widget class="QGroupBox" name="handheldGroup">
+ <property name="maximumSize">
+ <size>
+ <width>16777215</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Console Mode</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>8</number>
+ </property>
+ <property name="topMargin">
+ <number>6</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QRadioButton" name="radioDocked">
+ <property name="text">
+ <string>Docked</string>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QRadioButton" name="radioUndocked">
+ <property name="text">
+ <string>Undocked</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroup">
+ <property name="title">
+ <string>Vibration</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="vibrationButton">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="motionGroup">
+ <property name="title">
+ <string>Motion</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="motionButton">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignVCenter">
+ <widget class="QWidget" name="connectedControllers" native="true">
+ <layout class="QGridLayout" name="gridLayout_2">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer_9">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <property name="rightMargin">
+ <number>0</number>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </spacer>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ <property name="spacing">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
+ <item row="1" column="2">
+ <widget class="QCheckBox" name="checkboxPlayer2Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Controllers</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="4">
+ <widget class="QCheckBox" name="checkboxPlayer4Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="3">
+ <widget class="QCheckBox" name="checkboxPlayer3Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="5">
+ <widget class="QCheckBox" name="checkboxPlayer5Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="1">
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>1</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="7">
+ <widget class="QCheckBox" name="checkboxPlayer7Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="6">
+ <widget class="QCheckBox" name="checkboxPlayer6Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QCheckBox" name="checkboxPlayer1Connected">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="checked">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="8">
+ <widget class="QCheckBox" name="checkboxPlayer8Connected">
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="2">
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>2</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="3">
+ <widget class="QLabel" name="label_4">
+ <property name="text">
+ <string>3</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="4">
+ <widget class="QLabel" name="label_5">
+ <property name="text">
+ <string>4</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="5">
+ <widget class="QLabel" name="label_6">
+ <property name="text">
+ <string>5</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="6">
+ <widget class="QLabel" name="label_7">
+ <property name="text">
+ <string>6</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="7">
+ <widget class="QLabel" name="label_8">
+ <property name="text">
+ <string>7</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="8">
+ <widget class="QLabel" name="label_9">
+ <property name="text">
+ <string>8</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLabel" name="label_10">
+ <property name="text">
+ <string>Connected</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignBottom">
+ <widget class="QPushButton" name="buttonRestoreDefaults">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignBottom">
+ <widget class="QPushButton" name="buttonClearAll">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="sizeIncrement">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
</item>
</layout>
</widget>
<resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>ConfigureInput</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>294</x>
- <y>553</y>
- </hint>
- <hint type="destinationlabel">
- <x>191</x>
- <y>287</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>ConfigureInput</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>294</x>
- <y>553</y>
- </hint>
- <hint type="destinationlabel">
- <x>191</x>
- <y>287</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <connections/>
</ui>
diff --git a/src/yuzu/configuration/configure_input_advanced.cpp b/src/yuzu/configuration/configure_input_advanced.cpp
new file mode 100644
index 000000000..abaf03630
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.cpp
@@ -0,0 +1,171 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QColorDialog>
+#include "core/core.h"
+#include "core/settings.h"
+#include "ui_configure_input_advanced.h"
+#include "yuzu/configuration/configure_input_advanced.h"
+
+ConfigureInputAdvanced::ConfigureInputAdvanced(QWidget* parent)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputAdvanced>()) {
+ ui->setupUi(this);
+
+ controllers_color_buttons = {{
+ {
+ ui->player1_left_body_button,
+ ui->player1_left_buttons_button,
+ ui->player1_right_body_button,
+ ui->player1_right_buttons_button,
+ },
+ {
+ ui->player2_left_body_button,
+ ui->player2_left_buttons_button,
+ ui->player2_right_body_button,
+ ui->player2_right_buttons_button,
+ },
+ {
+ ui->player3_left_body_button,
+ ui->player3_left_buttons_button,
+ ui->player3_right_body_button,
+ ui->player3_right_buttons_button,
+ },
+ {
+ ui->player4_left_body_button,
+ ui->player4_left_buttons_button,
+ ui->player4_right_body_button,
+ ui->player4_right_buttons_button,
+ },
+ {
+ ui->player5_left_body_button,
+ ui->player5_left_buttons_button,
+ ui->player5_right_body_button,
+ ui->player5_right_buttons_button,
+ },
+ {
+ ui->player6_left_body_button,
+ ui->player6_left_buttons_button,
+ ui->player6_right_body_button,
+ ui->player6_right_buttons_button,
+ },
+ {
+ ui->player7_left_body_button,
+ ui->player7_left_buttons_button,
+ ui->player7_right_body_button,
+ ui->player7_right_buttons_button,
+ },
+ {
+ ui->player8_left_body_button,
+ ui->player8_left_buttons_button,
+ ui->player8_right_body_button,
+ ui->player8_right_buttons_button,
+ },
+ }};
+
+ for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
+ auto& color_buttons = controllers_color_buttons[player_idx];
+ for (std::size_t button_idx = 0; button_idx < color_buttons.size(); ++button_idx) {
+ connect(color_buttons[button_idx], &QPushButton::clicked, this,
+ [this, player_idx, button_idx] {
+ OnControllerButtonClick(player_idx, button_idx);
+ });
+ }
+ }
+
+ connect(ui->mouse_enabled, &QCheckBox::stateChanged, this,
+ &ConfigureInputAdvanced::UpdateUIEnabled);
+ connect(ui->debug_enabled, &QCheckBox::stateChanged, this,
+ &ConfigureInputAdvanced::UpdateUIEnabled);
+ connect(ui->touchscreen_enabled, &QCheckBox::stateChanged, this,
+ &ConfigureInputAdvanced::UpdateUIEnabled);
+
+ connect(ui->debug_configure, &QPushButton::clicked, this,
+ [this] { CallDebugControllerDialog(); });
+ connect(ui->mouse_advanced, &QPushButton::clicked, this, [this] { CallMouseConfigDialog(); });
+ connect(ui->touchscreen_advanced, &QPushButton::clicked, this,
+ [this] { CallTouchscreenConfigDialog(); });
+ connect(ui->buttonMotionTouch, &QPushButton::clicked, this,
+ &ConfigureInputAdvanced::CallMotionTouchConfigDialog);
+
+ LoadConfiguration();
+}
+
+ConfigureInputAdvanced::~ConfigureInputAdvanced() = default;
+
+void ConfigureInputAdvanced::OnControllerButtonClick(std::size_t player_idx,
+ std::size_t button_idx) {
+ const QColor new_bg_color = QColorDialog::getColor(controllers_colors[player_idx][button_idx]);
+ if (!new_bg_color.isValid()) {
+ return;
+ }
+ controllers_colors[player_idx][button_idx] = new_bg_color;
+ controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
+ QStringLiteral("background-color: %1; min-width: 60px;")
+ .arg(controllers_colors[player_idx][button_idx].name()));
+}
+
+void ConfigureInputAdvanced::ApplyConfiguration() {
+ for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
+ auto& player = Settings::values.players.GetValue()[player_idx];
+ std::array<u32, 4> colors{};
+ std::transform(controllers_colors[player_idx].begin(), controllers_colors[player_idx].end(),
+ colors.begin(), [](QColor color) { return color.rgb(); });
+
+ player.body_color_left = colors[0];
+ player.button_color_left = colors[1];
+ player.body_color_right = colors[2];
+ player.button_color_right = colors[3];
+ }
+
+ Settings::values.debug_pad_enabled = ui->debug_enabled->isChecked();
+ Settings::values.mouse_enabled = ui->mouse_enabled->isChecked();
+ Settings::values.keyboard_enabled = ui->keyboard_enabled->isChecked();
+ Settings::values.touchscreen.enabled = ui->touchscreen_enabled->isChecked();
+}
+
+void ConfigureInputAdvanced::LoadConfiguration() {
+ for (std::size_t player_idx = 0; player_idx < controllers_color_buttons.size(); ++player_idx) {
+ auto& player = Settings::values.players.GetValue()[player_idx];
+ std::array<u32, 4> colors = {
+ player.body_color_left,
+ player.button_color_left,
+ player.body_color_right,
+ player.button_color_right,
+ };
+
+ std::transform(colors.begin(), colors.end(), controllers_colors[player_idx].begin(),
+ [](u32 rgb) { return QColor::fromRgb(rgb); });
+
+ for (std::size_t button_idx = 0; button_idx < colors.size(); ++button_idx) {
+ controllers_color_buttons[player_idx][button_idx]->setStyleSheet(
+ QStringLiteral("background-color: %1; min-width: 60px;")
+ .arg(controllers_colors[player_idx][button_idx].name()));
+ }
+ }
+
+ ui->debug_enabled->setChecked(Settings::values.debug_pad_enabled);
+ ui->mouse_enabled->setChecked(Settings::values.mouse_enabled);
+ ui->keyboard_enabled->setChecked(Settings::values.keyboard_enabled);
+ ui->touchscreen_enabled->setChecked(Settings::values.touchscreen.enabled);
+
+ UpdateUIEnabled();
+}
+
+void ConfigureInputAdvanced::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QWidget::changeEvent(event);
+}
+
+void ConfigureInputAdvanced::RetranslateUI() {
+ ui->retranslateUi(this);
+}
+
+void ConfigureInputAdvanced::UpdateUIEnabled() {
+ ui->mouse_advanced->setEnabled(ui->mouse_enabled->isChecked());
+ ui->debug_configure->setEnabled(ui->debug_enabled->isChecked());
+ ui->touchscreen_advanced->setEnabled(ui->touchscreen_enabled->isChecked());
+}
diff --git a/src/yuzu/configuration/configure_input_advanced.h b/src/yuzu/configuration/configure_input_advanced.h
new file mode 100644
index 000000000..3083d55c1
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.h
@@ -0,0 +1,46 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <QWidget>
+
+class QColor;
+class QPushButton;
+
+namespace Ui {
+class ConfigureInputAdvanced;
+}
+
+class ConfigureInputAdvanced : public QWidget {
+ Q_OBJECT
+
+public:
+ explicit ConfigureInputAdvanced(QWidget* parent = nullptr);
+ ~ConfigureInputAdvanced() override;
+
+ void ApplyConfiguration();
+
+signals:
+ void CallDebugControllerDialog();
+ void CallMouseConfigDialog();
+ void CallTouchscreenConfigDialog();
+ void CallMotionTouchConfigDialog();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+ void UpdateUIEnabled();
+
+ void OnControllerButtonClick(std::size_t player_idx, std::size_t button_idx);
+
+ void LoadConfiguration();
+
+ std::unique_ptr<Ui::ConfigureInputAdvanced> ui;
+
+ std::array<std::array<QColor, 4>, 8> controllers_colors;
+ std::array<std::array<QPushButton*, 4>, 8> controllers_color_buttons;
+};
diff --git a/src/yuzu/configuration/configure_input_advanced.ui b/src/yuzu/configuration/configure_input_advanced.ui
new file mode 100644
index 000000000..a880a7c68
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_advanced.ui
@@ -0,0 +1,2688 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureInputAdvanced</class>
+ <widget class="QWidget" name="ConfigureInputAdvanced">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>710</width>
+ <height>580</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Input</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="mainInputAdvanced" native="true">
+ <layout class="QHBoxLayout" name="main" stretch="1,1">
+ <property name="spacing">
+ <number>9</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="leftInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="leftLayout" stretch="0">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="joyconColorsGroup">
+ <property name="title">
+ <string>Joycon Colors</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" stretch="1,1">
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="topLeftInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player12Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player1Group">
+ <property name="title">
+ <string>Player 1</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player1LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_14">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_66">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_67">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player1RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_14">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_64">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player1RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_65">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player1_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player2Group">
+ <property name="title">
+ <string>Player 2</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player2LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_15">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_70">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_71">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player2RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_15">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_68">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player2RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_69">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player2_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player34Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player3Group">
+ <property name="title">
+ <string>Player 3</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player3LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_16">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_74">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_75">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player3RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_16">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_72">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player3RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_73">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player3_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player4Group">
+ <property name="title">
+ <string>Player 4</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_16">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player4LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_17">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_78">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_79">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player4RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_17">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_76">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player4RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_77">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player4_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="bottomLeftInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player56Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player5Group">
+ <property name="title">
+ <string>Player 5</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player5LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_10">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_50">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_51">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player5RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_10">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_48">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player5RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_49">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player5_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player6Group">
+ <property name="title">
+ <string>Player 6</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player6LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_11">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_54">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_55">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player6RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_11">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_52">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player6RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_53">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player6_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player78Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_7">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="player7Group">
+ <property name="title">
+ <string>Player 7</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player7LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_12">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_58">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_59">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player7RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_12">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_56">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player7RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_57">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player7_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="player8Group">
+ <property name="title">
+ <string>Player 8</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>6</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>6</number>
+ </property>
+ <property name="bottomMargin">
+ <number>6</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player8LeftJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsLeftJoyconVerticalLayout_13">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8LeftBodyGroup">
+ <property name="title">
+ <string>L Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_62">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_left_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8LeftButtonsGroup">
+ <property name="title">
+ <string>L Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_63">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_left_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player8RightJoycon" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsRightJoyconVerticalLayout_13">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8RightBodyGroup">
+ <property name="title">
+ <string>R Body</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_60">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_right_body_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="player8RightButtonsGroup">
+ <property name="title">
+ <string>R Button</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_61">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="player8_right_buttons_button">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="rightInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="rightLayout" stretch="1,1">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="topRightInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="gridGroupBox_3">
+ <property name="title">
+ <string>Other</string>
+ </property>
+ <layout class="QGridLayout" name="gridLayout_3">
+ <item row="0" column="0">
+ <widget class="QCheckBox" name="keyboard_enabled">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Keyboard</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="2">
+ <widget class="QPushButton" name="touchscreen_advanced">
+ <property name="text">
+ <string>Advanced</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <spacer name="horizontalSpacer_8">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>76</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="1" column="2">
+ <widget class="QPushButton" name="mouse_advanced">
+ <property name="text">
+ <string>Advanced</string>
+ </property>
+ </widget>
+ </item>
+ <item row="4" column="0">
+ <widget class="QCheckBox" name="touchscreen_enabled">
+ <property name="text">
+ <string>Touchscreen</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QCheckBox" name="mouse_enabled">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>23</height>
+ </size>
+ </property>
+ <property name="text">
+ <string>Mouse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="0">
+ <widget class="QLabel" name="motion_touch">
+ <property name="text">
+ <string>Motion / Touch</string>
+ </property>
+ </widget>
+ </item>
+ <item row="6" column="2">
+ <widget class="QPushButton" name="buttonMotionTouch">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="0">
+ <widget class="QCheckBox" name="debug_enabled">
+ <property name="text">
+ <string>Debug Controller</string>
+ </property>
+ </widget>
+ </item>
+ <item row="5" column="2">
+ <widget class="QPushButton" name="debug_configure">
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="bottomRightInputAdvanced" native="true">
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="mainVerticalSpacer">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>40</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources>
+ </resources>
+ <connections/>
+</ui>
diff --git a/src/yuzu/configuration/configure_input_player.cpp b/src/yuzu/configuration/configure_input_player.cpp
index 597defe8c..56ab32a35 100644
--- a/src/yuzu/configuration/configure_input_player.cpp
+++ b/src/yuzu/configuration/configure_input_player.cpp
@@ -4,19 +4,28 @@
#include <algorithm>
#include <memory>
+#include <thread>
#include <utility>
-#include <QColorDialog>
#include <QGridLayout>
+#include <QInputDialog>
#include <QKeyEvent>
#include <QMenu>
#include <QMessageBox>
#include <QTimer>
-#include "common/assert.h"
#include "common/param_package.h"
+#include "core/core.h"
+#include "core/hle/service/hid/controllers/npad.h"
+#include "core/hle/service/hid/hid.h"
+#include "core/hle/service/sm/sm.h"
+#include "input_common/gcadapter/gc_poller.h"
#include "input_common/main.h"
+#include "input_common/udp/udp.h"
#include "ui_configure_input_player.h"
#include "yuzu/configuration/config.h"
#include "yuzu/configuration/configure_input_player.h"
+#include "yuzu/configuration/configure_vibration.h"
+#include "yuzu/configuration/input_profiles.h"
+#include "yuzu/util/limitable_input_dialog.h"
const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
ConfigureInputPlayer::analog_sub_buttons{{
@@ -24,20 +33,74 @@ const std::array<std::string, ConfigureInputPlayer::ANALOG_SUB_BUTTONS_NUM>
"down",
"left",
"right",
- "modifier",
}};
-static void LayerGridElements(QGridLayout* grid, QWidget* item, QWidget* onTopOf) {
- const int index1 = grid->indexOf(item);
- const int index2 = grid->indexOf(onTopOf);
- int row, column, rowSpan, columnSpan;
- grid->getItemPosition(index2, &row, &column, &rowSpan, &columnSpan);
- grid->takeAt(index1);
- grid->addWidget(item, row, column, rowSpan, columnSpan);
+namespace {
+
+constexpr std::size_t HANDHELD_INDEX = 8;
+
+void UpdateController(Settings::ControllerType controller_type, std::size_t npad_index,
+ bool connected) {
+ Core::System& system{Core::System::GetInstance()};
+ if (!system.IsPoweredOn()) {
+ return;
+ }
+ Service::SM::ServiceManager& sm = system.ServiceManager();
+
+ auto& npad =
+ sm.GetService<Service::HID::Hid>("hid")
+ ->GetAppletResource()
+ ->GetController<Service::HID::Controller_NPad>(Service::HID::HidController::NPad);
+
+ npad.UpdateControllerAt(npad.MapSettingsTypeToNPad(controller_type), npad_index, connected);
+}
+
+/// Maps the controller type combobox index to Controller Type enum
+constexpr Settings::ControllerType GetControllerTypeFromIndex(int index) {
+ switch (index) {
+ case 0:
+ default:
+ return Settings::ControllerType::ProController;
+ case 1:
+ return Settings::ControllerType::DualJoyconDetached;
+ case 2:
+ return Settings::ControllerType::LeftJoycon;
+ case 3:
+ return Settings::ControllerType::RightJoycon;
+ case 4:
+ return Settings::ControllerType::Handheld;
+ }
+}
+
+/// Maps the Controller Type enum to controller type combobox index
+constexpr int GetIndexFromControllerType(Settings::ControllerType type) {
+ switch (type) {
+ case Settings::ControllerType::ProController:
+ default:
+ return 0;
+ case Settings::ControllerType::DualJoyconDetached:
+ return 1;
+ case Settings::ControllerType::LeftJoycon:
+ return 2;
+ case Settings::ControllerType::RightJoycon:
+ return 3;
+ case Settings::ControllerType::Handheld:
+ return 4;
+ }
}
-static QString GetKeyName(int key_code) {
+QString GetKeyName(int key_code) {
switch (key_code) {
+ case Qt::LeftButton:
+ return QObject::tr("Click 0");
+ case Qt::RightButton:
+ return QObject::tr("Click 1");
+ case Qt::MiddleButton:
+ return QObject::tr("Click 2");
+ case Qt::BackButton:
+ return QObject::tr("Click 3");
+ case Qt::ForwardButton:
+ return QObject::tr("Click 4");
case Qt::Key_Shift:
return QObject::tr("Shift");
case Qt::Key_Control:
@@ -51,9 +114,16 @@ static QString GetKeyName(int key_code) {
}
}
-static void SetAnalogButton(const Common::ParamPackage& input_param,
- Common::ParamPackage& analog_param, const std::string& button_name) {
- if (analog_param.Get("engine", "") != "analog_from_button") {
+void SetAnalogParam(const Common::ParamPackage& input_param, Common::ParamPackage& analog_param,
+ const std::string& button_name) {
+ // The poller returned a complete axis, so set all the buttons
+ if (input_param.Has("axis_x") && input_param.Has("axis_y")) {
+ analog_param = input_param;
+ return;
+ }
+ // Check if the current configuration has either no engine or an axis binding.
+ // Clears out the old binding and adds one with analog_from_button.
+ if (!analog_param.Has("engine") || analog_param.Has("axis_x") || analog_param.Has("axis_y")) {
analog_param = {
{"engine", "analog_from_button"},
};
@@ -61,7 +131,7 @@ static void SetAnalogButton(const Common::ParamPackage& input_param,
analog_param.Set(button_name, input_param.Serialize());
}
-static QString ButtonToText(const Common::ParamPackage& param) {
+QString ButtonToText(const Common::ParamPackage& param) {
if (!param.Has("engine")) {
return QObject::tr("[not set]");
}
@@ -84,6 +154,14 @@ static QString ButtonToText(const Common::ParamPackage& param) {
return GetKeyName(param.Get("code", 0));
}
+ if (param.Get("engine", "") == "cemuhookudp") {
+ if (param.Has("pad_index")) {
+ const QString motion_str = QString::fromStdString(param.Get("pad_index", ""));
+ return QObject::tr("Motion %1").arg(motion_str);
+ }
+ return GetKeyName(param.Get("code", 0));
+ }
+
if (param.Get("engine", "") == "sdl") {
if (param.Has("hat")) {
const QString hat_str = QString::fromStdString(param.Get("hat", ""));
@@ -111,7 +189,7 @@ static QString ButtonToText(const Common::ParamPackage& param) {
return QObject::tr("[unknown]");
}
-static QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
+QString AnalogToText(const Common::ParamPackage& param, const std::string& dir) {
if (!param.Has("engine")) {
return QObject::tr("[not set]");
}
@@ -161,22 +239,26 @@ static QString AnalogToText(const Common::ParamPackage& param, const std::string
}
return QObject::tr("[unknown]");
}
-
-ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
- debug(debug), timeout_timer(std::make_unique<QTimer>()),
- poll_timer(std::make_unique<QTimer>()) {
+} // namespace
+
+ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_index,
+ QWidget* bottom_row,
+ InputCommon::InputSubsystem* input_subsystem_,
+ InputProfiles* profiles_, bool debug)
+ : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputPlayer>()), player_index(player_index),
+ debug(debug), input_subsystem{input_subsystem_}, profiles(profiles_),
+ timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()),
+ bottom_row(bottom_row) {
ui->setupUi(this);
+
setFocusPolicy(Qt::ClickFocus);
button_map = {
- ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
- ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
- ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
- ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
- ui->buttonLStickLeft, ui->buttonLStickUp, ui->buttonLStickRight, ui->buttonLStickDown,
- ui->buttonRStickLeft, ui->buttonRStickUp, ui->buttonRStickRight, ui->buttonRStickDown,
- ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
+ ui->buttonA, ui->buttonB, ui->buttonX, ui->buttonY,
+ ui->buttonLStick, ui->buttonRStick, ui->buttonL, ui->buttonR,
+ ui->buttonZL, ui->buttonZR, ui->buttonPlus, ui->buttonMinus,
+ ui->buttonDpadLeft, ui->buttonDpadUp, ui->buttonDpadRight, ui->buttonDpadDown,
+ ui->buttonSL, ui->buttonSR, ui->buttonHome, ui->buttonScreenshot,
};
analog_map_buttons = {{
@@ -185,122 +267,67 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ui->buttonLStickDown,
ui->buttonLStickLeft,
ui->buttonLStickRight,
- ui->buttonLStickMod,
},
{
ui->buttonRStickUp,
ui->buttonRStickDown,
ui->buttonRStickLeft,
ui->buttonRStickRight,
- ui->buttonRStickMod,
},
}};
- debug_hidden = {
- ui->buttonSL, ui->labelSL,
- ui->buttonSR, ui->labelSR,
- ui->buttonLStick, ui->labelLStickPressed,
- ui->buttonRStick, ui->labelRStickPressed,
- ui->buttonHome, ui->labelHome,
- ui->buttonScreenshot, ui->labelScreenshot,
+ motion_map = {
+ ui->buttonMotionLeft,
+ ui->buttonMotionRight,
};
- auto layout = Settings::values.players[player_index].type;
- if (debug)
- layout = Settings::ControllerType::DualJoycon;
-
- switch (layout) {
- case Settings::ControllerType::ProController:
- case Settings::ControllerType::DualJoycon:
- layout_hidden = {
- ui->buttonSL,
- ui->labelSL,
- ui->buttonSR,
- ui->labelSR,
- };
- break;
- case Settings::ControllerType::LeftJoycon:
- layout_hidden = {
- ui->right_body_button,
- ui->right_buttons_button,
- ui->right_body_label,
- ui->right_buttons_label,
- ui->buttonR,
- ui->labelR,
- ui->buttonZR,
- ui->labelZR,
- ui->labelHome,
- ui->buttonHome,
- ui->buttonPlus,
- ui->labelPlus,
- ui->RStick,
- ui->faceButtons,
- };
- break;
- case Settings::ControllerType::RightJoycon:
- layout_hidden = {
- ui->left_body_button, ui->left_buttons_button,
- ui->left_body_label, ui->left_buttons_label,
- ui->buttonL, ui->labelL,
- ui->buttonZL, ui->labelZL,
- ui->labelScreenshot, ui->buttonScreenshot,
- ui->buttonMinus, ui->labelMinus,
- ui->LStick, ui->Dpad,
- };
- break;
- }
-
- if (debug || layout == Settings::ControllerType::ProController) {
- ui->controller_color->hide();
- } else {
- if (layout == Settings::ControllerType::LeftJoycon ||
- layout == Settings::ControllerType::RightJoycon) {
- ui->horizontalSpacer_4->setGeometry({0, 0, 0, 0});
-
- LayerGridElements(ui->buttons, ui->shoulderButtons, ui->Dpad);
- LayerGridElements(ui->buttons, ui->misc, ui->RStick);
- LayerGridElements(ui->buttons, ui->Dpad, ui->faceButtons);
- LayerGridElements(ui->buttons, ui->RStick, ui->LStick);
- }
- }
-
- for (auto* widget : layout_hidden)
- widget->setVisible(false);
-
- analog_map_stick = {ui->buttonLStickAnalog, ui->buttonRStickAnalog};
- analog_map_deadzone_and_modifier_slider = {ui->sliderLStickDeadzoneAndModifier,
- ui->sliderRStickDeadzoneAndModifier};
- analog_map_deadzone_and_modifier_slider_label = {ui->labelLStickDeadzoneAndModifier,
- ui->labelRStickDeadzoneAndModifier};
-
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
- auto* const button = button_map[button_id];
- if (button == nullptr) {
- continue;
- }
-
- button->setContextMenuPolicy(Qt::CustomContextMenu);
+ analog_map_deadzone_label = {ui->labelLStickDeadzone, ui->labelRStickDeadzone};
+ analog_map_deadzone_slider = {ui->sliderLStickDeadzone, ui->sliderRStickDeadzone};
+ analog_map_modifier_groupbox = {ui->buttonLStickModGroup, ui->buttonRStickModGroup};
+ analog_map_modifier_button = {ui->buttonLStickMod, ui->buttonRStickMod};
+ analog_map_modifier_label = {ui->labelLStickModifierRange, ui->labelRStickModifierRange};
+ analog_map_modifier_slider = {ui->sliderLStickModifierRange, ui->sliderRStickModifierRange};
+ analog_map_range_groupbox = {ui->buttonLStickRangeGroup, ui->buttonRStickRangeGroup};
+ analog_map_range_spinbox = {ui->spinboxLStickRange, ui->spinboxRStickRange};
+
+ const auto ConfigureButtonClick = [&](QPushButton* button, Common::ParamPackage* param,
+ int default_val, InputCommon::Polling::DeviceType type) {
connect(button, &QPushButton::clicked, [=, this] {
HandleClick(
- button_map[button_id],
+ button,
[=, this](Common::ParamPackage params) {
- // Workaround for ZL & ZR for analog triggers like on XBOX controllors.
- // Analog triggers (from controllers like the XBOX controller) would not
- // work due to a different range of their signals (from 0 to 255 on
- // analog triggers instead of -32768 to 32768 on analog joysticks). The
- // SDL driver misinterprets analog triggers as analog joysticks.
+ // Workaround for ZL & ZR for analog triggers like on XBOX
+ // controllers. Analog triggers (from controllers like the XBOX
+ // controller) would not work due to a different range of their
+ // signals (from 0 to 255 on analog triggers instead of -32768 to
+ // 32768 on analog joysticks). The SDL driver misinterprets analog
+ // triggers as analog joysticks.
// TODO: reinterpret the signal range for analog triggers to map the
- // values correctly. This is required for the correct emulation of the
- // analog triggers of the GameCube controller.
- if (button_id == Settings::NativeButton::ZL ||
- button_id == Settings::NativeButton::ZR) {
+ // values correctly. This is required for the correct emulation of
+ // the analog triggers of the GameCube controller.
+ if (button == ui->buttonZL || button == ui->buttonZR) {
params.Set("direction", "+");
params.Set("threshold", "0.5");
}
- buttons_param[button_id] = std::move(params);
+ *param = std::move(params);
},
- InputCommon::Polling::DeviceType::Button);
+ type);
});
+ };
+
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
+ auto* const button = button_map[button_id];
+
+ if (button == nullptr) {
+ continue;
+ }
+
+ ConfigureButtonClick(button_map[button_id], &buttons_param[button_id],
+ Config::default_buttons[button_id],
+ InputCommon::Polling::DeviceType::Button);
+
+ button->setContextMenuPolicy(Qt::CustomContextMenu);
+
connect(button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
QMenu context_menu;
@@ -308,100 +335,198 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
buttons_param[button_id].Clear();
button_map[button_id]->setText(tr("[not set]"));
});
- context_menu.addAction(tr("Restore Default"), [&] {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
- button_map[button_id]->setText(ButtonToText(buttons_param[button_id]));
- });
context_menu.exec(button_map[button_id]->mapToGlobal(menu_location));
});
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+ auto* const button = motion_map[motion_id];
+ if (button == nullptr) {
+ continue;
+ }
+
+ ConfigureButtonClick(motion_map[motion_id], &motions_param[motion_id],
+ Config::default_motions[motion_id],
+ InputCommon::Polling::DeviceType::Motion);
+
+ button->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ connect(button, &QPushButton::customContextMenuRequested,
+ [=, this](const QPoint& menu_location) {
+ QMenu context_menu;
+ context_menu.addAction(tr("Clear"), [&] {
+ motions_param[motion_id].Clear();
+ motion_map[motion_id]->setText(tr("[not set]"));
+ });
+ context_menu.exec(motion_map[motion_id]->mapToGlobal(menu_location));
+ });
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
+
if (analog_button == nullptr) {
continue;
}
- analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
connect(analog_button, &QPushButton::clicked, [=, this] {
+ if (!map_analog_stick_accepted) {
+ map_analog_stick_accepted =
+ QMessageBox::information(
+ this, tr("Map Analog Stick"),
+ tr("After pressing OK, first move your joystick horizontally, and then "
+ "vertically.\nTo invert the axes, first move your joystick "
+ "vertically, and then horizontally."),
+ QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok;
+ if (!map_analog_stick_accepted) {
+ return;
+ }
+ }
HandleClick(
analog_map_buttons[analog_id][sub_button_id],
[=, this](const Common::ParamPackage& params) {
- SetAnalogButton(params, analogs_param[analog_id],
- analog_sub_buttons[sub_button_id]);
+ SetAnalogParam(params, analogs_param[analog_id],
+ analog_sub_buttons[sub_button_id]);
},
- InputCommon::Polling::DeviceType::Button);
+ InputCommon::Polling::DeviceType::AnalogPreferred);
});
+
+ analog_button->setContextMenuPolicy(Qt::CustomContextMenu);
+
connect(analog_button, &QPushButton::customContextMenuRequested,
[=, this](const QPoint& menu_location) {
QMenu context_menu;
context_menu.addAction(tr("Clear"), [&] {
- analogs_param[analog_id].Erase(analog_sub_buttons[sub_button_id]);
+ analogs_param[analog_id].Clear();
analog_map_buttons[analog_id][sub_button_id]->setText(tr("[not set]"));
});
- context_menu.addAction(tr("Restore Default"), [&] {
- Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
- Config::default_analogs[analog_id][sub_button_id])};
- SetAnalogButton(params, analogs_param[analog_id],
- analog_sub_buttons[sub_button_id]);
- analog_map_buttons[analog_id][sub_button_id]->setText(AnalogToText(
- analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
- });
context_menu.exec(analog_map_buttons[analog_id][sub_button_id]->mapToGlobal(
menu_location));
});
}
- connect(analog_map_stick[analog_id], &QPushButton::clicked, [=, this] {
- if (QMessageBox::information(
- this, tr("Information"),
- tr("After pressing OK, first move your joystick horizontally, "
- "and then vertically."),
- QMessageBox::Ok | QMessageBox::Cancel) == QMessageBox::Ok) {
- HandleClick(
- analog_map_stick[analog_id],
- [=, this](const Common::ParamPackage& params) {
- analogs_param[analog_id] = params;
- },
- InputCommon::Polling::DeviceType::Analog);
- }
+
+ // Handle clicks for the modifier buttons as well.
+ connect(analog_map_modifier_button[analog_id], &QPushButton::clicked, [=, this] {
+ HandleClick(
+ analog_map_modifier_button[analog_id],
+ [=, this](const Common::ParamPackage& params) {
+ analogs_param[analog_id].Set("modifier", params.Serialize());
+ },
+ InputCommon::Polling::DeviceType::Button);
});
- connect(analog_map_deadzone_and_modifier_slider[analog_id], &QSlider::valueChanged,
+ analog_map_modifier_button[analog_id]->setContextMenuPolicy(Qt::CustomContextMenu);
+
+ connect(analog_map_modifier_button[analog_id], &QPushButton::customContextMenuRequested,
+ [=, this](const QPoint& menu_location) {
+ QMenu context_menu;
+ context_menu.addAction(tr("Clear"), [&] {
+ analogs_param[analog_id].Set("modifier", "");
+ analog_map_modifier_button[analog_id]->setText(tr("[not set]"));
+ });
+ context_menu.exec(
+ analog_map_modifier_button[analog_id]->mapToGlobal(menu_location));
+ });
+
+ connect(analog_map_range_spinbox[analog_id], qOverload<int>(&QSpinBox::valueChanged),
[=, this] {
- const float slider_value =
- analog_map_deadzone_and_modifier_slider[analog_id]->value();
- if (analogs_param[analog_id].Get("engine", "") == "sdl" ||
- analogs_param[analog_id].Get("engine", "") == "gcpad") {
- analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
- tr("Deadzone: %1%").arg(slider_value));
- analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
- } else {
- analog_map_deadzone_and_modifier_slider_label[analog_id]->setText(
- tr("Modifier Scale: %1%").arg(slider_value));
- analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
- }
+ const auto spinbox_value = analog_map_range_spinbox[analog_id]->value();
+ analogs_param[analog_id].Set("range", spinbox_value / 100.0f);
+ });
+
+ connect(analog_map_deadzone_slider[analog_id], &QSlider::valueChanged, [=, this] {
+ const auto slider_value = analog_map_deadzone_slider[analog_id]->value();
+ analog_map_deadzone_label[analog_id]->setText(tr("Deadzone: %1%").arg(slider_value));
+ analogs_param[analog_id].Set("deadzone", slider_value / 100.0f);
+ });
+
+ connect(analog_map_modifier_slider[analog_id], &QSlider::valueChanged, [=, this] {
+ const auto slider_value = analog_map_modifier_slider[analog_id]->value();
+ analog_map_modifier_label[analog_id]->setText(
+ tr("Modifier Range: %1%").arg(slider_value));
+ analogs_param[analog_id].Set("modifier_scale", slider_value / 100.0f);
+ });
+ }
+
+ // Player Connected checkbox
+ connect(ui->groupConnectedController, &QGroupBox::toggled,
+ [this](bool checked) { emit Connected(checked); });
+
+ // Set up controller type. Only Player 1 can choose Handheld.
+ ui->comboControllerType->clear();
+
+ QStringList controller_types = {
+ tr("Pro Controller"),
+ tr("Dual Joycons"),
+ tr("Left Joycon"),
+ tr("Right Joycon"),
+ };
+
+ if (player_index == 0) {
+ controller_types.append(tr("Handheld"));
+ connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged),
+ [this](int index) {
+ emit HandheldStateChanged(GetControllerTypeFromIndex(index) ==
+ Settings::ControllerType::Handheld);
});
}
- connect(ui->buttonClearAll, &QPushButton::clicked, [this] { ClearAll(); });
- connect(ui->buttonRestoreDefaults, &QPushButton::clicked, [this] { RestoreDefaults(); });
+ if (debug || player_index == 9) {
+ ui->groupConnectedController->setCheckable(false);
+ }
+
+ // The Debug Controller can only choose the Pro Controller.
+ if (debug) {
+ ui->buttonScreenshot->setEnabled(false);
+ ui->buttonHome->setEnabled(false);
+ QStringList debug_controller_types = {
+ tr("Pro Controller"),
+ };
+ ui->comboControllerType->addItems(debug_controller_types);
+ } else {
+ ui->comboControllerType->addItems(controller_types);
+ }
+
+ UpdateControllerIcon();
+ UpdateControllerAvailableButtons();
+ UpdateMotionButtons();
+ connect(ui->comboControllerType, qOverload<int>(&QComboBox::currentIndexChanged), [this](int) {
+ UpdateControllerIcon();
+ UpdateControllerAvailableButtons();
+ UpdateMotionButtons();
+ });
+
+ connect(ui->comboDevices, qOverload<int>(&QComboBox::activated), this,
+ &ConfigureInputPlayer::UpdateMappingWithDefaults);
+
+ ui->comboDevices->setCurrentIndex(-1);
+
+ ui->buttonRefreshDevices->setIcon(QIcon::fromTheme(QStringLiteral("view-refresh")));
+ connect(ui->buttonRefreshDevices, &QPushButton::clicked,
+ [this] { emit RefreshInputDevices(); });
timeout_timer->setSingleShot(true);
connect(timeout_timer.get(), &QTimer::timeout, [this] { SetPollingResult({}, true); });
connect(poll_timer.get(), &QTimer::timeout, [this] {
Common::ParamPackage params;
- if (InputCommon::GetGCButtons()->IsPolling()) {
- params = InputCommon::GetGCButtons()->GetNextInput();
- if (params.Has("engine")) {
+ if (input_subsystem->GetGCButtons()->IsPolling()) {
+ params = input_subsystem->GetGCButtons()->GetNextInput();
+ if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
}
- if (InputCommon::GetGCAnalogs()->IsPolling()) {
- params = InputCommon::GetGCAnalogs()->GetNextInput();
+ if (input_subsystem->GetGCAnalogs()->IsPolling()) {
+ params = input_subsystem->GetGCAnalogs()->GetNextInput();
+ if (params.Has("engine") && IsInputAcceptable(params)) {
+ SetPollingResult(params, false);
+ return;
+ }
+ }
+ if (input_subsystem->GetUDPMotions()->IsPolling()) {
+ params = input_subsystem->GetUDPMotions()->GetNextInput();
if (params.Has("engine")) {
SetPollingResult(params, false);
return;
@@ -409,27 +534,25 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
}
for (auto& poller : device_pollers) {
params = poller->GetNextInput();
- if (params.Has("engine")) {
+ if (params.Has("engine") && IsInputAcceptable(params)) {
SetPollingResult(params, false);
return;
}
}
});
- controller_color_buttons = {
- ui->left_body_button,
- ui->left_buttons_button,
- ui->right_body_button,
- ui->right_buttons_button,
- };
+ UpdateInputProfiles();
- for (std::size_t i = 0; i < controller_color_buttons.size(); ++i) {
- connect(controller_color_buttons[i], &QPushButton::clicked, this,
- [this, i] { OnControllerButtonClick(static_cast<int>(i)); });
- }
+ connect(ui->buttonProfilesNew, &QPushButton::clicked, this,
+ &ConfigureInputPlayer::CreateProfile);
+ connect(ui->buttonProfilesDelete, &QPushButton::clicked, this,
+ &ConfigureInputPlayer::DeleteProfile);
+ connect(ui->comboProfiles, qOverload<int>(&QComboBox::activated), this,
+ &ConfigureInputPlayer::LoadProfile);
+ connect(ui->buttonProfilesSave, &QPushButton::clicked, this,
+ &ConfigureInputPlayer::SaveProfile);
LoadConfiguration();
- resize(0, 0);
// TODO(wwylele): enable this when we actually emulate it
ui->buttonHome->setEnabled(false);
@@ -438,27 +561,72 @@ ConfigureInputPlayer::ConfigureInputPlayer(QWidget* parent, std::size_t player_i
ConfigureInputPlayer::~ConfigureInputPlayer() = default;
void ConfigureInputPlayer::ApplyConfiguration() {
- auto& buttons =
- debug ? Settings::values.debug_pad_buttons : Settings::values.players[player_index].buttons;
- auto& analogs =
- debug ? Settings::values.debug_pad_analogs : Settings::values.players[player_index].analogs;
+ auto& player = Settings::values.players.GetValue()[player_index];
+ auto& buttons = debug ? Settings::values.debug_pad_buttons : player.buttons;
+ auto& analogs = debug ? Settings::values.debug_pad_analogs : player.analogs;
std::transform(buttons_param.begin(), buttons_param.end(), buttons.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); });
std::transform(analogs_param.begin(), analogs_param.end(), analogs.begin(),
[](const Common::ParamPackage& param) { return param.Serialize(); });
- if (debug)
+ if (debug) {
+ return;
+ }
+
+ auto& motions = player.motions;
+
+ std::transform(motions_param.begin(), motions_param.end(), motions.begin(),
+ [](const Common::ParamPackage& param) { return param.Serialize(); });
+
+ const auto controller_type =
+ GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+ const auto player_connected = ui->groupConnectedController->isChecked() &&
+ controller_type != Settings::ControllerType::Handheld;
+
+ if (player.controller_type == controller_type && player.connected == player_connected) {
+ // Set vibration devices in the event that the input device has changed.
+ ConfigureVibration::SetVibrationDevices(player_index);
return;
+ }
+
+ // Disconnect the controller first.
+ UpdateController(controller_type, player_index, false);
+
+ player.controller_type = controller_type;
+ player.connected = player_connected;
+
+ ConfigureVibration::SetVibrationDevices(player_index);
+
+ // Handheld
+ if (player_index == 0) {
+ auto& handheld = Settings::values.players.GetValue()[HANDHELD_INDEX];
+ if (controller_type == Settings::ControllerType::Handheld) {
+ handheld = player;
+ }
+ handheld.connected = ui->groupConnectedController->isChecked() &&
+ controller_type == Settings::ControllerType::Handheld;
+ UpdateController(Settings::ControllerType::Handheld, HANDHELD_INDEX, handheld.connected);
+ }
+
+ if (!player.connected) {
+ return;
+ }
- std::array<u32, 4> colors{};
- std::transform(controller_colors.begin(), controller_colors.end(), colors.begin(),
- [](QColor color) { return color.rgb(); });
+ // This emulates a delay between disconnecting and reconnecting controllers as some games
+ // do not respond to a change in controller type if it was instantaneous.
+ using namespace std::chrono_literals;
+ std::this_thread::sleep_for(20ms);
- Settings::values.players[player_index].body_color_left = colors[0];
- Settings::values.players[player_index].button_color_left = colors[1];
- Settings::values.players[player_index].body_color_right = colors[2];
- Settings::values.players[player_index].button_color_right = colors[3];
+ UpdateController(controller_type, player_index, player_connected);
+}
+
+void ConfigureInputPlayer::showEvent(QShowEvent* event) {
+ if (bottom_row == nullptr) {
+ return;
+ }
+ QWidget::showEvent(event);
+ ui->main->addWidget(bottom_row);
}
void ConfigureInputPlayer::changeEvent(QEvent* event) {
@@ -466,24 +634,16 @@ void ConfigureInputPlayer::changeEvent(QEvent* event) {
RetranslateUI();
}
- QDialog::changeEvent(event);
+ QWidget::changeEvent(event);
}
void ConfigureInputPlayer::RetranslateUI() {
ui->retranslateUi(this);
- UpdateButtonLabels();
-}
-
-void ConfigureInputPlayer::OnControllerButtonClick(int i) {
- const QColor new_bg_color = QColorDialog::getColor(controller_colors[i]);
- if (!new_bg_color.isValid())
- return;
- controller_colors[i] = new_bg_color;
- controller_color_buttons[i]->setStyleSheet(
- QStringLiteral("QPushButton { background-color: %1 }").arg(controller_colors[i].name()));
+ UpdateUI();
}
void ConfigureInputPlayer::LoadConfiguration() {
+ auto& player = Settings::values.players.GetValue()[player_index];
if (debug) {
std::transform(Settings::values.debug_pad_buttons.begin(),
Settings::values.debug_pad_buttons.end(), buttons_param.begin(),
@@ -492,68 +652,109 @@ void ConfigureInputPlayer::LoadConfiguration() {
Settings::values.debug_pad_analogs.end(), analogs_param.begin(),
[](const std::string& str) { return Common::ParamPackage(str); });
} else {
- std::transform(Settings::values.players[player_index].buttons.begin(),
- Settings::values.players[player_index].buttons.end(), buttons_param.begin(),
+ std::transform(player.buttons.begin(), player.buttons.end(), buttons_param.begin(),
+ [](const std::string& str) { return Common::ParamPackage(str); });
+ std::transform(player.analogs.begin(), player.analogs.end(), analogs_param.begin(),
[](const std::string& str) { return Common::ParamPackage(str); });
- std::transform(Settings::values.players[player_index].analogs.begin(),
- Settings::values.players[player_index].analogs.end(), analogs_param.begin(),
+ std::transform(player.motions.begin(), player.motions.end(), motions_param.begin(),
[](const std::string& str) { return Common::ParamPackage(str); });
}
- UpdateButtonLabels();
+ UpdateUI();
+ UpdateInputDeviceCombobox();
- if (debug)
+ if (debug) {
return;
+ }
- std::array<u32, 4> colors = {
- Settings::values.players[player_index].body_color_left,
- Settings::values.players[player_index].button_color_left,
- Settings::values.players[player_index].body_color_right,
- Settings::values.players[player_index].button_color_right,
- };
+ ui->comboControllerType->setCurrentIndex(static_cast<int>(player.controller_type));
+ ui->groupConnectedController->setChecked(
+ player.connected ||
+ (player_index == 0 && Settings::values.players.GetValue()[HANDHELD_INDEX].connected));
+}
- std::transform(colors.begin(), colors.end(), controller_colors.begin(),
- [](u32 rgb) { return QColor::fromRgb(rgb); });
+void ConfigureInputPlayer::ConnectPlayer(bool connected) {
+ ui->groupConnectedController->setChecked(connected);
+}
- for (std::size_t i = 0; i < colors.size(); ++i) {
- controller_color_buttons[i]->setStyleSheet(
- QStringLiteral("QPushButton { background-color: %1 }")
- .arg(controller_colors[i].name()));
+void ConfigureInputPlayer::UpdateInputDeviceCombobox() {
+ // Skip input device persistence if "Input Devices" is set to "Any".
+ if (ui->comboDevices->currentIndex() == 0) {
+ UpdateInputDevices();
+ return;
}
-}
-void ConfigureInputPlayer::RestoreDefaults() {
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
- buttons_param[button_id] = Common::ParamPackage{
- InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+ // Find the first button that isn't empty.
+ const auto button_param =
+ std::find_if(buttons_param.begin(), buttons_param.end(),
+ [](const Common::ParamPackage param) { return param.Has("engine"); });
+ const bool buttons_empty = button_param == buttons_param.end();
+
+ const auto current_engine = button_param->Get("engine", "");
+ const auto current_guid = button_param->Get("guid", "");
+ const auto current_port = button_param->Get("port", "");
+
+ const bool is_keyboard_mouse = current_engine == "keyboard" || current_engine == "mouse";
+
+ UpdateInputDevices();
+
+ if (buttons_empty) {
+ return;
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
- Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
- Config::default_analogs[analog_id][sub_button_id])};
- SetAnalogButton(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
+ const bool all_one_device =
+ std::all_of(buttons_param.begin(), buttons_param.end(),
+ [current_engine, current_guid, current_port,
+ is_keyboard_mouse](const Common::ParamPackage param) {
+ if (is_keyboard_mouse) {
+ return !param.Has("engine") || param.Get("engine", "") == "keyboard" ||
+ param.Get("engine", "") == "mouse";
+ }
+ return !param.Has("engine") || (param.Get("engine", "") == current_engine &&
+ param.Get("guid", "") == current_guid &&
+ param.Get("port", "") == current_port);
+ });
+
+ if (all_one_device) {
+ if (is_keyboard_mouse) {
+ ui->comboDevices->setCurrentIndex(1);
+ return;
}
+ const auto devices_it = std::find_if(
+ input_devices.begin(), input_devices.end(),
+ [current_engine, current_guid, current_port](const Common::ParamPackage param) {
+ return param.Get("class", "") == current_engine &&
+ param.Get("guid", "") == current_guid &&
+ param.Get("port", "") == current_port;
+ });
+ const int device_index =
+ devices_it != input_devices.end()
+ ? static_cast<int>(std::distance(input_devices.begin(), devices_it))
+ : 0;
+ ui->comboDevices->setCurrentIndex(device_index);
+ } else {
+ ui->comboDevices->setCurrentIndex(0);
}
+}
- UpdateButtonLabels();
- ApplyConfiguration();
+void ConfigureInputPlayer::RestoreDefaults() {
+ UpdateMappingWithDefaults();
}
void ConfigureInputPlayer::ClearAll() {
- for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; button_id++) {
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
const auto* const button = button_map[button_id];
- if (button == nullptr || !button->isEnabled()) {
+ if (button == nullptr) {
continue;
}
buttons_param[button_id].Clear();
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
const auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
- if (analog_button == nullptr || !analog_button->isEnabled()) {
+ if (analog_button == nullptr) {
continue;
}
@@ -561,17 +762,30 @@ void ConfigureInputPlayer::ClearAll() {
}
}
- UpdateButtonLabels();
- ApplyConfiguration();
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+ const auto* const motion_button = motion_map[motion_id];
+ if (motion_button == nullptr) {
+ continue;
+ }
+
+ motions_param[motion_id].Clear();
+ }
+
+ UpdateUI();
+ UpdateInputDevices();
}
-void ConfigureInputPlayer::UpdateButtonLabels() {
- for (int button = 0; button < Settings::NativeButton::NumButtons; button++) {
+void ConfigureInputPlayer::UpdateUI() {
+ for (int button = 0; button < Settings::NativeButton::NumButtons; ++button) {
button_map[button]->setText(ButtonToText(buttons_param[button]));
}
- for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; analog_id++) {
- for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; sub_button_id++) {
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+ motion_map[motion_id]->setText(ButtonToText(motions_param[motion_id]));
+ }
+
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
auto* const analog_button = analog_map_buttons[analog_id][sub_button_id];
if (analog_button == nullptr) {
@@ -581,99 +795,329 @@ void ConfigureInputPlayer::UpdateButtonLabels() {
analog_button->setText(
AnalogToText(analogs_param[analog_id], analog_sub_buttons[sub_button_id]));
}
- analog_map_stick[analog_id]->setText(tr("Set Analog Stick"));
+ analog_map_modifier_button[analog_id]->setText(
+ ButtonToText(Common::ParamPackage{analogs_param[analog_id].Get("modifier", "")}));
+
+ const auto deadzone_label = analog_map_deadzone_label[analog_id];
+ const auto deadzone_slider = analog_map_deadzone_slider[analog_id];
+ const auto modifier_groupbox = analog_map_modifier_groupbox[analog_id];
+ const auto modifier_label = analog_map_modifier_label[analog_id];
+ const auto modifier_slider = analog_map_modifier_slider[analog_id];
+ const auto range_groupbox = analog_map_range_groupbox[analog_id];
+ const auto range_spinbox = analog_map_range_spinbox[analog_id];
+
+ int slider_value;
auto& param = analogs_param[analog_id];
- auto* const analog_stick_slider = analog_map_deadzone_and_modifier_slider[analog_id];
- auto* const analog_stick_slider_label =
- analog_map_deadzone_and_modifier_slider_label[analog_id];
-
- if (param.Has("engine")) {
- if (param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad") {
- if (!param.Has("deadzone")) {
- param.Set("deadzone", 0.1f);
- }
+ const bool is_controller =
+ param.Get("engine", "") == "sdl" || param.Get("engine", "") == "gcpad";
- analog_stick_slider->setValue(static_cast<int>(param.Get("deadzone", 0.1f) * 100));
- if (analog_stick_slider->value() == 0) {
- analog_stick_slider_label->setText(tr("Deadzone: 0%"));
- }
- } else {
- if (!param.Has("modifier_scale")) {
- param.Set("modifier_scale", 0.5f);
- }
+ if (is_controller) {
+ if (!param.Has("deadzone")) {
+ param.Set("deadzone", 0.1f);
+ }
+ slider_value = static_cast<int>(param.Get("deadzone", 0.1f) * 100);
+ deadzone_label->setText(tr("Deadzone: %1%").arg(slider_value));
+ deadzone_slider->setValue(slider_value);
+ if (!param.Has("range")) {
+ param.Set("range", 1.0f);
+ }
+ range_spinbox->setValue(static_cast<int>(param.Get("range", 1.0f) * 100));
+ } else {
+ if (!param.Has("modifier_scale")) {
+ param.Set("modifier_scale", 0.5f);
+ }
+ slider_value = static_cast<int>(param.Get("modifier_scale", 0.5f) * 100);
+ modifier_label->setText(tr("Modifier Range: %1%").arg(slider_value));
+ modifier_slider->setValue(slider_value);
+ }
- analog_stick_slider->setValue(
- static_cast<int>(param.Get("modifier_scale", 0.5f) * 100));
- if (analog_stick_slider->value() == 0) {
- analog_stick_slider_label->setText(tr("Modifier Scale: 0%"));
- }
+ deadzone_label->setVisible(is_controller);
+ deadzone_slider->setVisible(is_controller);
+ modifier_groupbox->setVisible(!is_controller);
+ modifier_label->setVisible(!is_controller);
+ modifier_slider->setVisible(!is_controller);
+ range_groupbox->setVisible(is_controller);
+ }
+}
+
+void ConfigureInputPlayer::UpdateInputDevices() {
+ input_devices = input_subsystem->GetInputDevices();
+ ui->comboDevices->clear();
+ for (auto device : input_devices) {
+ ui->comboDevices->addItem(QString::fromStdString(device.Get("display", "Unknown")), {});
+ }
+}
+
+void ConfigureInputPlayer::UpdateControllerIcon() {
+ // We aren't using Qt's built in theme support here since we aren't drawing an icon (and its
+ // "nonstandard" to use an image through the icon support)
+ const QString stylesheet = [this] {
+ switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
+ case Settings::ControllerType::ProController:
+ return QStringLiteral("image: url(:/controller/pro_controller%0)");
+ case Settings::ControllerType::DualJoyconDetached:
+ return QStringLiteral("image: url(:/controller/dual_joycon%0)");
+ case Settings::ControllerType::LeftJoycon:
+ return QStringLiteral("image: url(:/controller/single_joycon_left_vertical%0)");
+ case Settings::ControllerType::RightJoycon:
+ return QStringLiteral("image: url(:/controller/single_joycon_right_vertical%0)");
+ case Settings::ControllerType::Handheld:
+ return QStringLiteral("image: url(:/controller/handheld%0)");
+ default:
+ return QString{};
+ }
+ }();
+
+ const QString theme = [] {
+ if (QIcon::themeName().contains(QStringLiteral("dark"))) {
+ return QStringLiteral("_dark");
+ } else if (QIcon::themeName().contains(QStringLiteral("midnight"))) {
+ return QStringLiteral("_midnight");
+ } else {
+ return QString{};
+ }
+ }();
+
+ ui->controllerFrame->setStyleSheet(stylesheet.arg(theme));
+}
+
+void ConfigureInputPlayer::UpdateControllerAvailableButtons() {
+ auto layout = GetControllerTypeFromIndex(ui->comboControllerType->currentIndex());
+ if (debug) {
+ layout = Settings::ControllerType::ProController;
+ }
+
+ // List of all the widgets that will be hidden by any of the following layouts that need
+ // "unhidden" after the controller type changes
+ const std::array<QWidget*, 9> layout_show = {
+ ui->buttonShoulderButtonsSLSR,
+ ui->horizontalSpacerShoulderButtonsWidget,
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->buttonShoulderButtonsLeft,
+ ui->buttonMiscButtonsMinusScreenshot,
+ ui->bottomLeft,
+ ui->buttonShoulderButtonsRight,
+ ui->buttonMiscButtonsPlusHome,
+ ui->bottomRight,
+ };
+
+ for (auto* widget : layout_show) {
+ widget->show();
+ }
+
+ std::vector<QWidget*> layout_hidden;
+ switch (layout) {
+ case Settings::ControllerType::ProController:
+ case Settings::ControllerType::DualJoyconDetached:
+ case Settings::ControllerType::Handheld:
+ layout_hidden = {
+ ui->buttonShoulderButtonsSLSR,
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ };
+ break;
+ case Settings::ControllerType::LeftJoycon:
+ layout_hidden = {
+ ui->horizontalSpacerShoulderButtonsWidget2,
+ ui->buttonShoulderButtonsRight,
+ ui->buttonMiscButtonsPlusHome,
+ ui->bottomRight,
+ };
+ break;
+ case Settings::ControllerType::RightJoycon:
+ layout_hidden = {
+ ui->horizontalSpacerShoulderButtonsWidget,
+ ui->buttonShoulderButtonsLeft,
+ ui->buttonMiscButtonsMinusScreenshot,
+ ui->bottomLeft,
+ };
+ break;
+ }
+
+ for (auto* widget : layout_hidden) {
+ widget->hide();
+ }
+}
+
+void ConfigureInputPlayer::UpdateMotionButtons() {
+ if (debug) {
+ // Motion isn't used with the debug controller, hide both groupboxes.
+ ui->buttonMotionLeftGroup->hide();
+ ui->buttonMotionRightGroup->hide();
+ return;
+ }
+
+ // Show/hide the "Motion 1/2" groupboxes depending on the currently selected controller.
+ switch (GetControllerTypeFromIndex(ui->comboControllerType->currentIndex())) {
+ case Settings::ControllerType::ProController:
+ case Settings::ControllerType::LeftJoycon:
+ case Settings::ControllerType::Handheld:
+ // Show "Motion 1" and hide "Motion 2".
+ ui->buttonMotionLeftGroup->show();
+ ui->buttonMotionRightGroup->hide();
+ break;
+ case Settings::ControllerType::RightJoycon:
+ // Show "Motion 2" and hide "Motion 1".
+ ui->buttonMotionLeftGroup->hide();
+ ui->buttonMotionRightGroup->show();
+ break;
+ case Settings::ControllerType::DualJoyconDetached:
+ default:
+ // Show both "Motion 1/2".
+ ui->buttonMotionLeftGroup->show();
+ ui->buttonMotionRightGroup->show();
+ break;
+ }
+}
+
+void ConfigureInputPlayer::UpdateMappingWithDefaults() {
+ if (ui->comboDevices->currentIndex() == 0) {
+ return;
+ }
+
+ if (ui->comboDevices->currentIndex() == 1) {
+ // Reset keyboard bindings
+ for (int button_id = 0; button_id < Settings::NativeButton::NumButtons; ++button_id) {
+ buttons_param[button_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_buttons[button_id])};
+ }
+ for (int analog_id = 0; analog_id < Settings::NativeAnalog::NumAnalogs; ++analog_id) {
+ for (int sub_button_id = 0; sub_button_id < ANALOG_SUB_BUTTONS_NUM; ++sub_button_id) {
+ Common::ParamPackage params{InputCommon::GenerateKeyboardParam(
+ Config::default_analogs[analog_id][sub_button_id])};
+ SetAnalogParam(params, analogs_param[analog_id], analog_sub_buttons[sub_button_id]);
}
+
+ analogs_param[analog_id].Set("modifier", InputCommon::GenerateKeyboardParam(
+ Config::default_stick_mod[analog_id]));
+ }
+
+ for (int motion_id = 0; motion_id < Settings::NativeMotion::NumMotions; ++motion_id) {
+ motions_param[motion_id] = Common::ParamPackage{
+ InputCommon::GenerateKeyboardParam(Config::default_motions[motion_id])};
}
+
+ UpdateUI();
+ return;
+ }
+
+ // Reset controller bindings
+ const auto& device = input_devices[ui->comboDevices->currentIndex()];
+ auto button_mapping = input_subsystem->GetButtonMappingForDevice(device);
+ auto analog_mapping = input_subsystem->GetAnalogMappingForDevice(device);
+ for (std::size_t i = 0; i < buttons_param.size(); ++i) {
+ buttons_param[i] = button_mapping[static_cast<Settings::NativeButton::Values>(i)];
+ }
+ for (std::size_t i = 0; i < analogs_param.size(); ++i) {
+ analogs_param[i] = analog_mapping[static_cast<Settings::NativeAnalog::Values>(i)];
}
+
+ UpdateUI();
}
void ConfigureInputPlayer::HandleClick(
QPushButton* button, std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type) {
- button->setText(tr("[press key]"));
+ if (button == ui->buttonMotionLeft || button == ui->buttonMotionRight) {
+ button->setText(tr("Shake!"));
+ } else {
+ button->setText(tr("[waiting]"));
+ }
button->setFocus();
- // Keyboard keys can only be used as button devices
- want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
- if (want_keyboard_keys) {
- const auto iter = std::find(button_map.begin(), button_map.end(), button);
- ASSERT(iter != button_map.end());
- const auto index = std::distance(button_map.begin(), iter);
- ASSERT(index < Settings::NativeButton::NumButtons && index >= 0);
- }
+ // The first two input devices are always Any and Keyboard/Mouse. If the user filtered to a
+ // controller, then they don't want keyboard/mouse input
+ want_keyboard_mouse = ui->comboDevices->currentIndex() < 2;
input_setter = new_input_setter;
- device_pollers = InputCommon::Polling::GetPollers(type);
+ device_pollers = input_subsystem->GetPollers(type);
for (auto& poller : device_pollers) {
poller->Start();
}
- grabKeyboard();
- grabMouse();
+ QWidget::grabMouse();
+ QWidget::grabKeyboard();
+
if (type == InputCommon::Polling::DeviceType::Button) {
- InputCommon::GetGCButtons()->BeginConfiguration();
+ input_subsystem->GetGCButtons()->BeginConfiguration();
} else {
- InputCommon::GetGCAnalogs()->BeginConfiguration();
+ input_subsystem->GetGCAnalogs()->BeginConfiguration();
}
- timeout_timer->start(5000); // Cancel after 5 seconds
- poll_timer->start(200); // Check for new inputs every 200ms
+
+ if (type == InputCommon::Polling::DeviceType::Motion) {
+ input_subsystem->GetUDPMotions()->BeginConfiguration();
+ }
+
+ timeout_timer->start(2500); // Cancel after 2.5 seconds
+ poll_timer->start(50); // Check for new inputs every 50ms
}
void ConfigureInputPlayer::SetPollingResult(const Common::ParamPackage& params, bool abort) {
- releaseKeyboard();
- releaseMouse();
timeout_timer->stop();
poll_timer->stop();
for (auto& poller : device_pollers) {
poller->Stop();
}
- InputCommon::GetGCButtons()->EndConfiguration();
- InputCommon::GetGCAnalogs()->EndConfiguration();
+ QWidget::releaseMouse();
+ QWidget::releaseKeyboard();
+
+ input_subsystem->GetGCButtons()->EndConfiguration();
+ input_subsystem->GetGCAnalogs()->EndConfiguration();
+
+ input_subsystem->GetUDPMotions()->EndConfiguration();
if (!abort) {
(*input_setter)(params);
}
- UpdateButtonLabels();
+ UpdateUI();
+ UpdateInputDeviceCombobox();
+
input_setter = std::nullopt;
}
+bool ConfigureInputPlayer::IsInputAcceptable(const Common::ParamPackage& params) const {
+ if (ui->comboDevices->currentIndex() == 0) {
+ return true;
+ }
+
+ // Keyboard/Mouse
+ if (ui->comboDevices->currentIndex() == 1) {
+ return params.Get("engine", "") == "keyboard" || params.Get("engine", "") == "mouse";
+ }
+
+ const auto current_input_device = input_devices[ui->comboDevices->currentIndex()];
+ return params.Get("engine", "") == current_input_device.Get("class", "") &&
+ params.Get("guid", "") == current_input_device.Get("guid", "") &&
+ params.Get("port", "") == current_input_device.Get("port", "");
+}
+
+void ConfigureInputPlayer::mousePressEvent(QMouseEvent* event) {
+ if (!input_setter || !event) {
+ return;
+ }
+
+ if (want_keyboard_mouse) {
+ SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
+ false);
+ } else {
+ // We don't want any mouse buttons, so don't stop polling
+ return;
+ }
+
+ SetPollingResult({}, true);
+}
+
void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
- if (want_keyboard_keys) {
+ if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
@@ -681,5 +1125,105 @@ void ConfigureInputPlayer::keyPressEvent(QKeyEvent* event) {
return;
}
}
+
SetPollingResult({}, true);
}
+
+void ConfigureInputPlayer::CreateProfile() {
+ const auto profile_name =
+ LimitableInputDialog::GetText(this, tr("New Profile"), tr("Enter a profile name:"), 1, 20);
+
+ if (profile_name.isEmpty()) {
+ return;
+ }
+
+ if (!InputProfiles::IsProfileNameValid(profile_name.toStdString())) {
+ QMessageBox::critical(this, tr("Create Input Profile"),
+ tr("The given profile name is not valid!"));
+ return;
+ }
+
+ ApplyConfiguration();
+
+ if (!profiles->CreateProfile(profile_name.toStdString(), player_index)) {
+ QMessageBox::critical(this, tr("Create Input Profile"),
+ tr("Failed to create the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
+ return;
+ }
+
+ emit RefreshInputProfiles(player_index);
+
+ ui->comboProfiles->addItem(profile_name);
+ ui->comboProfiles->setCurrentIndex(ui->comboProfiles->count() - 1);
+}
+
+void ConfigureInputPlayer::DeleteProfile() {
+ const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
+
+ if (profile_name.isEmpty()) {
+ return;
+ }
+
+ if (!profiles->DeleteProfile(profile_name.toStdString())) {
+ QMessageBox::critical(this, tr("Delete Input Profile"),
+ tr("Failed to delete the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
+ return;
+ }
+
+ emit RefreshInputProfiles(player_index);
+
+ ui->comboProfiles->removeItem(ui->comboProfiles->currentIndex());
+ ui->comboProfiles->setCurrentIndex(-1);
+}
+
+void ConfigureInputPlayer::LoadProfile() {
+ const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
+
+ if (profile_name.isEmpty()) {
+ return;
+ }
+
+ ApplyConfiguration();
+
+ if (!profiles->LoadProfile(profile_name.toStdString(), player_index)) {
+ QMessageBox::critical(this, tr("Load Input Profile"),
+ tr("Failed to load the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
+ return;
+ }
+
+ LoadConfiguration();
+}
+
+void ConfigureInputPlayer::SaveProfile() {
+ const QString profile_name = ui->comboProfiles->itemText(ui->comboProfiles->currentIndex());
+
+ if (profile_name.isEmpty()) {
+ return;
+ }
+
+ ApplyConfiguration();
+
+ if (!profiles->SaveProfile(profile_name.toStdString(), player_index)) {
+ QMessageBox::critical(this, tr("Save Input Profile"),
+ tr("Failed to save the input profile \"%1\"").arg(profile_name));
+ UpdateInputProfiles();
+ emit RefreshInputProfiles(player_index);
+ return;
+ }
+}
+
+void ConfigureInputPlayer::UpdateInputProfiles() {
+ ui->comboProfiles->clear();
+
+ for (const auto& profile_name : profiles->GetInputProfileNames()) {
+ ui->comboProfiles->addItem(QString::fromStdString(profile_name));
+ }
+
+ ui->comboProfiles->setCurrentIndex(-1);
+}
diff --git a/src/yuzu/configuration/configure_input_player.h b/src/yuzu/configuration/configure_input_player.h
index 95afa5375..23cf6f958 100644
--- a/src/yuzu/configuration/configure_input_player.h
+++ b/src/yuzu/configuration/configure_input_player.h
@@ -10,16 +10,27 @@
#include <optional>
#include <string>
-#include <QDialog>
+#include <QWidget>
#include "common/param_package.h"
#include "core/settings.h"
#include "ui_configure_input.h"
+class QCheckBox;
class QKeyEvent;
+class QLabel;
class QPushButton;
+class QSlider;
+class QSpinBox;
class QString;
class QTimer;
+class QWidget;
+
+class InputProfiles;
+
+namespace InputCommon {
+class InputSubsystem;
+}
namespace InputCommon::Polling {
class DevicePoller;
@@ -30,86 +41,165 @@ namespace Ui {
class ConfigureInputPlayer;
}
-class ConfigureInputPlayer : public QDialog {
+class ConfigureInputPlayer : public QWidget {
Q_OBJECT
public:
- explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, bool debug = false);
+ explicit ConfigureInputPlayer(QWidget* parent, std::size_t player_index, QWidget* bottom_row,
+ InputCommon::InputSubsystem* input_subsystem_,
+ InputProfiles* profiles_, bool debug = false);
~ConfigureInputPlayer() override;
- /// Save all button configurations to settings file
+ /// Save all button configurations to settings file.
void ApplyConfiguration();
-private:
- void changeEvent(QEvent* event) override;
- void RetranslateUI();
+ /// Set the connection state checkbox (used to sync state).
+ void ConnectPlayer(bool connected);
- void OnControllerButtonClick(int i);
+ /// Update the input devices combobox.
+ void UpdateInputDeviceCombobox();
+
+ /// Updates the list of controller profiles.
+ void UpdateInputProfiles();
- /// Load configuration settings.
- void LoadConfiguration();
/// Restore all buttons to their default values.
void RestoreDefaults();
- /// Clear all input configuration
+
+ /// Clear all input configuration.
void ClearAll();
- /// Update UI to reflect current configuration.
- void UpdateButtonLabels();
+signals:
+ /// Emitted when this controller is connected by the user.
+ void Connected(bool connected);
+ /// Emitted when the Handheld mode is selected (undocked with dual joycons attached).
+ void HandheldStateChanged(bool is_handheld);
+ /// Emitted when the input devices combobox is being refreshed.
+ void RefreshInputDevices();
+ /**
+ * Emitted when the input profiles combobox is being refreshed.
+ * The player_index represents the current player's index, and the profile combobox
+ * will not be updated for this index as they are already updated by other mechanisms.
+ */
+ void RefreshInputProfiles(std::size_t player_index);
+
+protected:
+ void showEvent(QShowEvent* event) override;
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ /// Load configuration settings.
+ void LoadConfiguration();
/// Called when the button was pressed.
void HandleClick(QPushButton* button,
std::function<void(const Common::ParamPackage&)> new_input_setter,
InputCommon::Polling::DeviceType type);
- /// Finish polling and configure input using the input_setter
+ /// Finish polling and configure input using the input_setter.
void SetPollingResult(const Common::ParamPackage& params, bool abort);
+ /// Checks whether a given input can be accepted.
+ bool IsInputAcceptable(const Common::ParamPackage& params) const;
+
+ /// Handle mouse button press events.
+ void mousePressEvent(QMouseEvent* event) override;
+
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
+ /// Update UI to reflect current configuration.
+ void UpdateUI();
+
+ /// Update the available input devices.
+ void UpdateInputDevices();
+
+ /// Update the current controller icon.
+ void UpdateControllerIcon();
+
+ /// Hides and disables controller settings based on the current controller type.
+ void UpdateControllerAvailableButtons();
+
+ /// Shows or hides motion groupboxes based on the current controller type.
+ void UpdateMotionButtons();
+
+ /// Gets the default controller mapping for this device and auto configures the input to match.
+ void UpdateMappingWithDefaults();
+
+ /// Creates a controller profile.
+ void CreateProfile();
+
+ /// Deletes the selected controller profile.
+ void DeleteProfile();
+
+ /// Loads the selected controller profile.
+ void LoadProfile();
+
+ /// Saves the current controller configuration into a selected controller profile.
+ void SaveProfile();
+
std::unique_ptr<Ui::ConfigureInputPlayer> ui;
std::size_t player_index;
bool debug;
+ InputCommon::InputSubsystem* input_subsystem;
+
+ InputProfiles* profiles;
+
std::unique_ptr<QTimer> timeout_timer;
std::unique_ptr<QTimer> poll_timer;
+ static constexpr int PLAYER_COUNT = 8;
+ std::array<QCheckBox*, PLAYER_COUNT> player_connected_checkbox;
+
/// This will be the the setting function when an input is awaiting configuration.
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
std::array<Common::ParamPackage, Settings::NativeButton::NumButtons> buttons_param;
std::array<Common::ParamPackage, Settings::NativeAnalog::NumAnalogs> analogs_param;
+ std::array<Common::ParamPackage, Settings::NativeMotion::NumMotions> motions_param;
- static constexpr int ANALOG_SUB_BUTTONS_NUM = 5;
+ static constexpr int ANALOG_SUB_BUTTONS_NUM = 4;
/// Each button input is represented by a QPushButton.
std::array<QPushButton*, Settings::NativeButton::NumButtons> button_map;
- std::vector<QWidget*> debug_hidden;
- std::vector<QWidget*> layout_hidden;
-
- /// A group of five QPushButtons represent one analog input. The buttons each represent up,
- /// down, left, right, and modifier, respectively.
+ /// A group of four QPushButtons represent one analog input. The buttons each represent up,
+ /// down, left, right, respectively.
std::array<std::array<QPushButton*, ANALOG_SUB_BUTTONS_NUM>, Settings::NativeAnalog::NumAnalogs>
analog_map_buttons;
- /// Analog inputs are also represented each with a single button, used to configure with an
- /// actual analog stick
- std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_stick;
- std::array<QSlider*, Settings::NativeAnalog::NumAnalogs>
- analog_map_deadzone_and_modifier_slider;
- std::array<QLabel*, Settings::NativeAnalog::NumAnalogs>
- analog_map_deadzone_and_modifier_slider_label;
+ /// Each motion input is represented by a QPushButton.
+ std::array<QPushButton*, Settings::NativeMotion::NumMotions> motion_map;
+
+ std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_label;
+ std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_deadzone_slider;
+ std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_groupbox;
+ std::array<QPushButton*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_button;
+ std::array<QLabel*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_label;
+ std::array<QSlider*, Settings::NativeAnalog::NumAnalogs> analog_map_modifier_slider;
+ std::array<QGroupBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_groupbox;
+ std::array<QSpinBox*, Settings::NativeAnalog::NumAnalogs> analog_map_range_spinbox;
static const std::array<std::string, ANALOG_SUB_BUTTONS_NUM> analog_sub_buttons;
std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+ /// A flag to indicate that the "Map Analog Stick" pop-up has been shown and accepted once.
+ bool map_analog_stick_accepted{};
+
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
- bool want_keyboard_keys = false;
+ bool want_keyboard_mouse{};
+
+ /// List of physical devices users can map with. If a SDL backed device is selected, then you
+ /// can use this device to get a default mapping.
+ std::vector<Common::ParamPackage> input_devices;
- std::array<QPushButton*, 4> controller_color_buttons;
- std::array<QColor, 4> controller_colors;
+ /// Bottom row is where console wide settings are held, and its "owned" by the parent
+ /// ConfigureInput widget. On show, add this widget to the main layout. This will change the
+ /// parent of the widget to this widget (but thats fine).
+ QWidget* bottom_row;
};
diff --git a/src/yuzu/configuration/configure_input_player.ui b/src/yuzu/configuration/configure_input_player.ui
index f27a77180..1e78b4c10 100644
--- a/src/yuzu/configuration/configure_input_player.ui
+++ b/src/yuzu/configuration/configure_input_player.ui
@@ -1,1243 +1,3094 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ConfigureInputPlayer</class>
- <widget class="QDialog" name="ConfigureInputPlayer">
+ <widget class="QWidget" name="ConfigureInputPlayer">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
- <width>408</width>
- <height>731</height>
+ <width>780</width>
+ <height>487</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Input</string>
</property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
<item>
- <layout class="QGridLayout" name="buttons">
- <item row="1" column="1">
- <widget class="QGroupBox" name="RStick">
- <property name="title">
- <string>Right Stick</string>
- </property>
- <property name="alignment">
- <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
- </property>
- <property name="flat">
- <bool>false</bool>
+ <layout class="QVBoxLayout" name="main">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="top" stretch="0,1,2">
+ <property name="spacing">
+ <number>3</number>
</property>
- <property name="checkable">
- <bool>false</bool>
+ <property name="topMargin">
+ <number>0</number>
</property>
- <layout class="QGridLayout" name="gridLayout_5">
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
+ <item>
+ <widget class="QGroupBox" name="groupConnectedController">
+ <property name="layoutDirection">
+ <enum>Qt::LeftToRight</enum>
+ </property>
+ <property name="title">
+ <string>Connect Controller</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
<item>
- <layout class="QHBoxLayout" name="buttonRStickDownHorizontalLayout">
+ <widget class="QComboBox" name="comboControllerType">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>21</height>
+ </size>
+ </property>
<item>
- <widget class="QLabel" name="labelRStickDown">
- <property name="text">
- <string>Down:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Pro Controller</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickDown">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickRightHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickRight">
- <property name="text">
- <string>Right:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Dual Joycons</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickRight">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="0" colspan="2">
- <widget class="QPushButton" name="buttonRStickAnalog">
- <property name="text">
- <string>Set Analog Stick</string>
- </property>
- </widget>
- </item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickLeftHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickLeft">
- <property name="text">
- <string>Left:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Left Joycon</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickLeft">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickUpHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickUp">
- <property name="text">
- <string>Up:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Right Joycon</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickUp">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="2" column="0">
- <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonRStickPressedHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickPressed">
- <property name="text">
- <string>Pressed:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Handheld</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStick">
- <property name="text">
- <string/>
- </property>
</widget>
</item>
</layout>
- </item>
- <item row="2" column="1">
- <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="devicesGroup">
+ <property name="title">
+ <string>Input Device</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
+ </property>
<item>
- <layout class="QHBoxLayout" name="buttonRStickModHorizontalLayout">
+ <widget class="QComboBox" name="comboDevices">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>21</height>
+ </size>
+ </property>
<item>
- <widget class="QLabel" name="labelRStickMod">
- <property name="text">
- <string>Modifier:</string>
- </property>
- </widget>
+ <property name="text">
+ <string>Any</string>
+ </property>
</item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRStickMod">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="4" column="0" colspan="2">
- <layout class="QVBoxLayout" name="sliderRStickDeadzoneAndModifierVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="sliderRStickDeadzoneAndModifierHorizontalLayout">
<item>
- <widget class="QLabel" name="labelRStickDeadzoneAndModifier">
- <property name="text">
- <string>Deadzone: 0</string>
- </property>
- <property name="alignment">
- <enum>Qt::AlignHCenter</enum>
- </property>
- </widget>
+ <property name="text">
+ <string>Keyboard/Mouse</string>
+ </property>
</item>
- </layout>
+ </widget>
</item>
<item>
- <widget class="QSlider" name="sliderRStickDeadzoneAndModifier">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
+ <widget class="QPushButton" name="buttonRefreshDevices">
+ <property name="minimumSize">
+ <size>
+ <width>21</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>21</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
</property>
</widget>
</item>
</layout>
- </item>
- <item row="5" column="0">
- <spacer name="RStick_verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="profilesGroup">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="title">
+ <string>Profile</string>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_4" stretch="2,0,0,0">
+ <property name="spacing">
+ <number>3</number>
</property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
+ <property name="leftMargin">
+ <number>5</number>
+ </property>
+ <property name="topMargin">
+ <number>5</number>
+ </property>
+ <property name="rightMargin">
+ <number>5</number>
+ </property>
+ <property name="bottomMargin">
+ <number>5</number>
</property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- <item row="0" column="1">
- <widget class="QGroupBox" name="Dpad">
- <property name="title">
- <string>Directional Pad</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_2">
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonDpadUpHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadUp">
- <property name="text">
- <string>Up:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item>
- <widget class="QPushButton" name="buttonDpadUp">
- <property name="text">
- <string/>
+ <widget class="QComboBox" name="comboProfiles">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>21</height>
+ </size>
</property>
</widget>
</item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonDpadDownHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadDown">
- <property name="text">
- <string>Down:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item>
- <widget class="QPushButton" name="buttonDpadDown">
+ <widget class="QPushButton" name="buttonProfilesSave">
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
<property name="text">
- <string/>
+ <string>Save</string>
</property>
</widget>
</item>
- </layout>
- </item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonDpadLeftHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadLeft">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Left:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item>
- <widget class="QPushButton" name="buttonDpadLeft">
+ <widget class="QPushButton" name="buttonProfilesNew">
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
<property name="text">
- <string/>
+ <string>New</string>
</property>
</widget>
</item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonDpadRightHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelDpadRight">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Right:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
<item>
- <widget class="QPushButton" name="buttonDpadRight">
+ <widget class="QPushButton" name="buttonProfilesDelete">
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
<property name="text">
- <string/>
+ <string>Delete</string>
</property>
</widget>
</item>
</layout>
- </item>
- </layout>
- </widget>
+ </widget>
+ </item>
+ </layout>
</item>
- <item row="0" column="0">
- <widget class="QGroupBox" name="faceButtons">
- <property name="title">
- <string>Face Buttons</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
+ <item>
+ <widget class="QFrame" name="bottom">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
</property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsAHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelA">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
+ <layout class="QHBoxLayout" name="_2">
+ <property name="sizeConstraint">
+ <enum>QLayout::SetMinimumSize</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="bottomLeft" native="true">
+ <layout class="QVBoxLayout" name="bottomLeftLayout" stretch="0,0,0,0">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="LStick">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Left Stick</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3">
+ <property name="spacing">
+ <number>0</number>
</property>
- <property name="text">
- <string>A:</string>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonA">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsBHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelB">
- <property name="minimumSize">
- <size>
- <width>80</width>
- <height>0</height>
- </size>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- <property name="text">
- <string>B:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonB">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsXHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelX">
- <property name="text">
- <string>X:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonX">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonFaceButtonsYHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelY">
- <property name="text">
- <string>Y:</string>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonY">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item row="5" column="0" colspan="2">
- <widget class="QGroupBox" name="controller_color">
- <property name="title">
- <string>Controller Color</string>
- </property>
- <layout class="QGridLayout" name="gridLayout_10" columnstretch="0,0,0,0,0,0,0">
- <item row="0" column="0">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="1">
- <widget class="QLabel" name="left_body_label">
- <property name="text">
- <string>Left Body</string>
- </property>
- </widget>
- </item>
- <item row="0" column="6">
- <spacer name="horizontalSpacer_3">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QLabel" name="left_buttons_label">
- <property name="minimumSize">
- <size>
- <width>90</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Left Buttons</string>
- </property>
- </widget>
- </item>
- <item row="1" column="5">
- <widget class="QPushButton" name="right_buttons_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="4">
- <widget class="QLabel" name="right_body_label">
- <property name="text">
- <string>Right Body</string>
- </property>
- </widget>
- </item>
- <item row="1" column="4">
- <widget class="QLabel" name="right_buttons_label">
- <property name="minimumSize">
- <size>
- <width>90</width>
- <height>0</height>
- </size>
- </property>
- <property name="text">
- <string>Right Buttons</string>
- </property>
- </widget>
- </item>
- <item row="1" column="2">
- <widget class="QPushButton" name="left_buttons_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="2">
- <widget class="QPushButton" name="left_body_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="5">
- <widget class="QPushButton" name="right_body_button">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="minimumSize">
- <size>
- <width>32</width>
- <height>0</height>
- </size>
- </property>
- <property name="maximumSize">
- <size>
- <width>40</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- <item row="0" column="3">
- <spacer name="horizontalSpacer_4">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- <item row="1" column="0">
- <widget class="QGroupBox" name="LStick">
- <property name="title">
- <string>Left Stick</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_4">
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickUpHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickUp">
- <property name="text">
- <string>Up:</string>
+ <item>
+ <widget class="QWidget" name="buttonLStickUpWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_20">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerLStickUpLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickUpGroup">
+ <property name="title">
+ <string>Up</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickUpVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickUp">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Up</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerLStickUpRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickLeftRightHorizontaLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickLeftGroup">
+ <property name="title">
+ <string>Left</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickLeft">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickRightGroup">
+ <property name="title">
+ <string>Right</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickRight">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Right</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonLStickDownWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_22">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerLStickDownLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickDownGroup">
+ <property name="title">
+ <string>Down</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickDown">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Down</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerLStickDownRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonLStickPressedModifierHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickPressedGroup">
+ <property name="title">
+ <string>Pressed</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStick">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Pressed</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonLStickModGroup">
+ <property name="title">
+ <string>Modifier</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonLStickMod">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Modifier</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonLStickRangeGroup">
+ <property name="title">
+ <string>Range</string>
+ </property>
+ <layout class="QHBoxLayout" name="buttonLStickRangeGroupHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="spinboxLStickRange">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>50</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="sliderLStickDeadzoneModifierRangeVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="sizeConstraint">
+ <enum>QLayout::SetDefaultConstraint</enum>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="sliderLStickDeadzoneHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickDeadzone">
+ <property name="text">
+ <string>Deadzone: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderLStickDeadzone">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="sliderLStickModifierRangeHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelLStickModifierRange">
+ <property name="text">
+ <string>Modifier Range: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderLStickModifierRange">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomLeft">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="Dpad">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>D-Pad</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_5">
+ <property name="spacing">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickUp">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="2">
- <layout class="QVBoxLayout" name="buttonLStickRightVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickRightHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickRight">
- <property name="text">
- <string>Right:</string>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickRight">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="4" column="1" colspan="2">
- <widget class="QPushButton" name="buttonLStickAnalog">
- <property name="text">
- <string>Set Analog Stick</string>
- </property>
- </widget>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonLStickLeftVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickLeftHorizontalLayout_2">
- <item>
- <widget class="QLabel" name="labelLStickLeft">
- <property name="text">
- <string>Left:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickLeft">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="2">
- <layout class="QVBoxLayout" name="buttonLStickDownVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickDownHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickDown">
- <property name="text">
- <string>Down:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickDown">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="2">
- <layout class="QVBoxLayout" name="buttonLStickModVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickModHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickMod">
- <property name="text">
- <string>Modifier:</string>
+ <property name="bottomMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStickMod">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item>
+ <widget class="QWidget" name="buttonDpadUpWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_23">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerDpadUpLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadUpGroup">
+ <property name="title">
+ <string>Up</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadUpVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadUp">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Up</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerDpadUpRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonDpadLeftRightHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadLeftGroup">
+ <property name="title">
+ <string>Left</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadLeft">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadRightGroup">
+ <property name="title">
+ <string>Right</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadRight">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Right</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonDpadDownWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_24">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerDpadDownLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonDpadDownGroup">
+ <property name="title">
+ <string>Down</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadDownVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonDpadDown">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Down</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerDpadDownRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomLeft_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
</item>
- <item row="3" column="1">
- <layout class="QVBoxLayout" name="buttonLStickPressedVerticalLayout" stretch="0,0">
- <item>
- <layout class="QHBoxLayout" name="buttonLStickPressedHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickPressed">
- <property name="text">
- <string>Pressed:</string>
+ <item>
+ <widget class="QWidget" name="bottomMiddle" native="true">
+ <layout class="QVBoxLayout" stretch="0,0,0">
+ <property name="spacing">
+ <number>6</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="shoulderButtons">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsLeft" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsLeftVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsButtonLGroup">
+ <property name="title">
+ <string>L</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonL">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>L</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsButtonZLGroup">
+ <property name="title">
+ <string>ZL</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonZL">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>ZL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidgetLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerShoulderButtons1">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonMiscButtonsMinusScreenshot" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsMinusScreenshotVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsMinusGroup">
+ <property name="title">
+ <string>Minus</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonMinus">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Minus</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsScreenshotGroup">
+ <property name="title">
+ <string>Capture</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonScreenshot">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Capture</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonMiscButtonsPlusHome" native="true">
+ <layout class="QVBoxLayout" name="buttonMiscButtonsPlusHomeVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsPlusGroup">
+ <property name="title">
+ <string>Plus</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonPlus">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Plus</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonMiscButtonsHomeGroup">
+ <property name="title">
+ <string>Home</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonHome">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Home</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget3" native="true">
+ <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget3Layout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerShoulderButtons2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsRight" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsRightVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsRGroup">
+ <property name="title">
+ <string>R</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonR">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>R</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonShoulderButtonsZRGroup">
+ <property name="title">
+ <string>ZR</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonZR">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>ZR</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="horizontalSpacerShoulderButtonsWidget2" native="true">
+ <layout class="QHBoxLayout" name="horizontalSpacerShoulderButtonsWidget2Layout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerShoulderButtons3">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonShoulderButtonsSLSR" native="true">
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLSRVerticalLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSLGroup">
+ <property name="title">
+ <string>SL</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSL">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>SL</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonShoulderButtonsSRGroup">
+ <property name="title">
+ <string>SR</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonSR">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>SR</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QFrame" name="controllerFrame">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="font">
+ <font>
+ <weight>75</weight>
+ <bold>true</bold>
+ </font>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">image: url(:/controller/pro);</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonLStick">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="5" column="1" colspan="2">
- <layout class="QVBoxLayout" name="sliderLStickDeadzoneAndModifierVerticalLayout">
- <property name="sizeConstraint">
- <enum>QLayout::SetDefaultConstraint</enum>
- </property>
- <item>
- <layout class="QHBoxLayout" name="sliderLStickDeadzoneAndModifierHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelLStickDeadzoneAndModifier">
- <property name="text">
- <string>Deadzone: 0</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- <property name="alignment">
- <enum>Qt::AlignHCenter</enum>
+ <property name="rightMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QSlider" name="sliderLStickDeadzoneAndModifier">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="6" column="1">
- <spacer name="LStick_verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- </item>
- <item row="3" column="0">
- <widget class="QGroupBox" name="shoulderButtons">
- <property name="title">
- <string>Shoulder Buttons</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_3">
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsLVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsLHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelL">
- <property name="text">
- <string>L:</string>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="miscButtons">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerMiscButtons1">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonMotionLeftGroup">
+ <property name="title">
+ <string>Motion 1</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadLeftVerticalLayout_2">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonMotionLeft">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonMotionRightGroup">
+ <property name="title">
+ <string>Motion 2</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonDpadRightVerticalLayout_2">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonMotionRight">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Right</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerMiscButtons4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
</item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsRVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsRHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelR">
- <property name="text">
- <string>R:</string>
+ <item>
+ <widget class="QWidget" name="bottomRight" native="true">
+ <layout class="QVBoxLayout" name="bottomRightLayout">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="faceButtons">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Face Buttons</string>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsZLVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsZLHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelZL">
- <property name="text">
- <string>ZL:</string>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonZL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsZRVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsZRHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelZR">
- <property name="text">
- <string>ZR:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonZR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="2">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSLVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsSLHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelSL">
- <property name="text">
- <string>SL:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonSL">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="2">
- <layout class="QVBoxLayout" name="buttonShoulderButtonsSRVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonShoulderButtonsSRHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelSR">
- <property name="text">
- <string>SR:</string>
+ <property name="bottomMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonSR">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- </layout>
- </widget>
- </item>
- <item row="3" column="1">
- <widget class="QGroupBox" name="misc">
- <property name="title">
- <string>Misc.</string>
- </property>
- <property name="flat">
- <bool>false</bool>
- </property>
- <property name="checkable">
- <bool>false</bool>
- </property>
- <layout class="QGridLayout" name="gridLayout_6">
- <item row="1" column="0">
- <layout class="QVBoxLayout" name="buttonMiscMinusVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscMinusHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelMinus">
- <property name="text">
- <string>Minus:</string>
+ <item>
+ <widget class="QWidget" name="buttonFaceButtonsBWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerBLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsXGroup">
+ <property name="title">
+ <string>X</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsXVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonX">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>X</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerBRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonFaceButtonsYAHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsYGroup">
+ <property name="title">
+ <string>Y</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsYVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonY">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Y</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsAGroup">
+ <property name="title">
+ <string>A</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsAVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonA">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>A</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonFaceButtonsXWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerXLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonFaceButtonsBWidget_2">
+ <property name="title">
+ <string>B</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonFaceButtonsBVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonB">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>B</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerXRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomRight">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="RStick">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="title">
+ <string>Right Stick</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter</set>
+ </property>
+ <property name="flat">
+ <bool>false</bool>
+ </property>
+ <property name="checkable">
+ <bool>false</bool>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <property name="spacing">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonMinus">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="3" column="1">
- <spacer name="verticalSpacer_2">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="0">
- <layout class="QVBoxLayout" name="buttonMiscPlusVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscPlusHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelPlus">
- <property name="text">
- <string>Plus:</string>
+ <property name="leftMargin">
+ <number>3</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonPlus">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="1">
- <layout class="QVBoxLayout" name="buttonMiscHomeVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscHomeHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelHome">
- <property name="text">
- <string>Home:</string>
+ <property name="topMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonHome">
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="1" column="1">
- <layout class="QVBoxLayout" name="buttonMiscScrCapVerticalLayout">
- <item>
- <layout class="QHBoxLayout" name="buttonMiscScrCapHorizontalLayout">
- <item>
- <widget class="QLabel" name="labelScreenshot">
- <property name="text">
- <string>Screen Capture:</string>
+ <property name="rightMargin">
+ <number>3</number>
</property>
- <property name="wordWrap">
- <bool>false</bool>
+ <property name="bottomMargin">
+ <number>0</number>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="buttonScreenshot">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="maximumSize">
- <size>
- <width>80</width>
- <height>16777215</height>
- </size>
- </property>
- <property name="text">
- <string/>
- </property>
- </widget>
- </item>
- </layout>
+ <item>
+ <widget class="QWidget" name="buttonRStickUpWidget" native="true">
+ <property name="minimumSize">
+ <size>
+ <width>0</width>
+ <height>0</height>
+ </size>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerRStickUpLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickUpGroup">
+ <property name="title">
+ <string>Up</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickUpVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickUp">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Up</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerRStickUpRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickLeftRightHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickLeftGroup">
+ <property name="title">
+ <string>Left</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickLeftVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickLeft">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Left</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickRightGroup">
+ <property name="title">
+ <string>Right</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickRightVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickRight">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Right</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="buttonRStickDownWidget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="spacing">
+ <number>0</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <spacer name="horizontalSpacerRStickDownLeft">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonRStickDownGroup">
+ <property name="title">
+ <string>Down</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickDownVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickDown">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Down</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacerRStickDownRight">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="buttonRStickPressedModifierHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="groupRStickPressed">
+ <property name="title">
+ <string>Pressed</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickPressedVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStick">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Pressed</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item alignment="Qt::AlignHCenter">
+ <widget class="QGroupBox" name="buttonRStickModGroup">
+ <property name="title">
+ <string>Modifier</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignCenter</set>
+ </property>
+ <layout class="QVBoxLayout" name="buttonRStickModVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QPushButton" name="buttonRStickMod">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="styleSheet">
+ <string notr="true">min-width: 68px;</string>
+ </property>
+ <property name="text">
+ <string>Modifier</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="buttonRStickRangeGroup">
+ <property name="title">
+ <string>Range</string>
+ </property>
+ <layout class="QHBoxLayout" name="buttonRStickRangeGroupHorizontalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="spinboxRStickRange">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>50</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QVBoxLayout" name="sliderRStickDeadzoneModifierRangeVerticalLayout">
+ <property name="spacing">
+ <number>3</number>
+ </property>
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>2</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="sliderRStickDeadzoneHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickDeadzone">
+ <property name="text">
+ <string>Deadzone: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderRStickDeadzone">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="sliderRStickModifierRangeHorizontalLayout">
+ <item>
+ <widget class="QLabel" name="labelRStickModifierRange">
+ <property name="text">
+ <string>Modifier Range: 0%</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignHCenter</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QSlider" name="sliderRStickModifierRange">
+ <property name="maximum">
+ <number>100</number>
+ </property>
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="verticalSpacerBottomRight_2">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>20</width>
+ <height>0</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ </layout>
+ </widget>
</item>
</layout>
</widget>
</item>
</layout>
</item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout"/>
- </item>
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QPushButton" name="buttonClearAll">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="sizeIncrement">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Clear All</string>
- </property>
- </widget>
- </item>
- <item>
- <widget class="QPushButton" name="buttonRestoreDefaults">
- <property name="sizePolicy">
- <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
- <horstretch>0</horstretch>
- <verstretch>0</verstretch>
- </sizepolicy>
- </property>
- <property name="sizeIncrement">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="baseSize">
- <size>
- <width>0</width>
- <height>0</height>
- </size>
- </property>
- <property name="layoutDirection">
- <enum>Qt::LeftToRight</enum>
- </property>
- <property name="text">
- <string>Restore Defaults</string>
- </property>
- </widget>
- </item>
- <item>
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
- </layout>
- </item>
</layout>
</widget>
- <resources/>
- <connections>
- <connection>
- <sender>buttonBox</sender>
- <signal>accepted()</signal>
- <receiver>ConfigureInputPlayer</receiver>
- <slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>371</x>
- <y>730</y>
- </hint>
- <hint type="destinationlabel">
- <x>229</x>
- <y>375</y>
- </hint>
- </hints>
- </connection>
- <connection>
- <sender>buttonBox</sender>
- <signal>rejected()</signal>
- <receiver>ConfigureInputPlayer</receiver>
- <slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>371</x>
- <y>730</y>
- </hint>
- <hint type="destinationlabel">
- <x>229</x>
- <y>375</y>
- </hint>
- </hints>
- </connection>
- </connections>
+ <resources>
+ <include location="../../../dist/icons/controller/controller.qrc"/>
+ </resources>
+ <connections/>
</ui>
diff --git a/src/yuzu/configuration/configure_input_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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include "ui_configure_input_profile_dialog.h"
+#include "yuzu/configuration/configure_input_player.h"
+#include "yuzu/configuration/configure_input_profile_dialog.h"
+
+ConfigureInputProfileDialog::ConfigureInputProfileDialog(
+ QWidget* parent, InputCommon::InputSubsystem* input_subsystem, InputProfiles* profiles)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureInputProfileDialog>()),
+ profile_widget(new ConfigureInputPlayer(this, 9, nullptr, input_subsystem, profiles, false)) {
+ ui->setupUi(this);
+
+ ui->controllerLayout->addWidget(profile_widget);
+
+ connect(ui->clear_all_button, &QPushButton::clicked, this,
+ [this] { profile_widget->ClearAll(); });
+ connect(ui->restore_defaults_button, &QPushButton::clicked, this,
+ [this] { profile_widget->RestoreDefaults(); });
+
+ RetranslateUI();
+}
+
+ConfigureInputProfileDialog::~ConfigureInputProfileDialog() = default;
+
+void ConfigureInputProfileDialog::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigureInputProfileDialog::RetranslateUI() {
+ ui->retranslateUi(this);
+}
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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QDialog>
+
+class QPushButton;
+
+class ConfigureInputPlayer;
+
+class InputProfiles;
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
+namespace Ui {
+class ConfigureInputProfileDialog;
+}
+
+class ConfigureInputProfileDialog : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureInputProfileDialog(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem,
+ InputProfiles* profiles);
+ ~ConfigureInputProfileDialog() override;
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ std::unique_ptr<Ui::ConfigureInputProfileDialog> ui;
+
+ ConfigureInputPlayer* profile_widget;
+};
diff --git a/src/yuzu/configuration/configure_input_profile_dialog.ui b/src/yuzu/configuration/configure_input_profile_dialog.ui
new file mode 100644
index 000000000..726cf6905
--- /dev/null
+++ b/src/yuzu/configuration/configure_input_profile_dialog.ui
@@ -0,0 +1,71 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureInputProfileDialog</class>
+ <widget class="QDialog" name="ConfigureInputProfileDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>70</width>
+ <height>540</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Create Input Profile</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <property name="spacing">
+ <number>2</number>
+ </property>
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <layout class="QHBoxLayout" name="controllerLayout"/>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QPushButton" name="clear_all_button">
+ <property name="text">
+ <string>Clear</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="restore_defaults_button">
+ <property name="text">
+ <string>Defaults</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureInputProfileDialog</receiver>
+ <slot>accept()</slot>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/configuration/configure_input_simple.cpp b/src/yuzu/configuration/configure_input_simple.cpp
deleted file mode 100644
index 0e0e8f113..000000000
--- a/src/yuzu/configuration/configure_input_simple.cpp
+++ /dev/null
@@ -1,152 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#include <array>
-#include <tuple>
-
-#include "ui_configure_input_simple.h"
-#include "yuzu/configuration/configure_input.h"
-#include "yuzu/configuration/configure_input_player.h"
-#include "yuzu/configuration/configure_input_simple.h"
-#include "yuzu/uisettings.h"
-
-namespace {
-
-template <typename Dialog, typename... Args>
-void CallConfigureDialog(ConfigureInputSimple* caller, Args&&... args) {
- caller->ApplyConfiguration();
- Dialog dialog(caller, std::forward<Args>(args)...);
-
- const auto res = dialog.exec();
- if (res == QDialog::Accepted) {
- dialog.ApplyConfiguration();
- }
-}
-
-// OnProfileSelect functions should (when applicable):
-// - Set controller types
-// - Set controller enabled
-// - Set docked mode
-// - Set advanced controller config/enabled (i.e. debug, kbd, mouse, touch)
-//
-// OnProfileSelect function should NOT however:
-// - Reset any button mappings
-// - Open any dialogs
-// - Block in any way
-
-constexpr std::size_t PLAYER_0_INDEX = 0;
-constexpr std::size_t HANDHELD_INDEX = 8;
-
-void HandheldOnProfileSelect() {
- Settings::values.players[HANDHELD_INDEX].connected = true;
- Settings::values.players[HANDHELD_INDEX].type = Settings::ControllerType::DualJoycon;
-
- for (std::size_t player = 0; player < HANDHELD_INDEX; ++player) {
- Settings::values.players[player].connected = false;
- }
-
- Settings::values.use_docked_mode = false;
- Settings::values.keyboard_enabled = false;
- Settings::values.mouse_enabled = false;
- Settings::values.debug_pad_enabled = false;
- Settings::values.touchscreen.enabled = true;
-}
-
-void DualJoyconsDockedOnProfileSelect() {
- Settings::values.players[PLAYER_0_INDEX].connected = true;
- Settings::values.players[PLAYER_0_INDEX].type = Settings::ControllerType::DualJoycon;
-
- for (std::size_t player = 1; player <= HANDHELD_INDEX; ++player) {
- Settings::values.players[player].connected = false;
- }
-
- Settings::values.use_docked_mode = true;
- Settings::values.keyboard_enabled = false;
- Settings::values.mouse_enabled = false;
- Settings::values.debug_pad_enabled = false;
- Settings::values.touchscreen.enabled = true;
-}
-
-// Name, OnProfileSelect (called when selected in drop down), OnConfigure (called when configure
-// is clicked)
-using InputProfile = std::tuple<const char*, void (*)(), void (*)(ConfigureInputSimple*)>;
-
-constexpr std::array<InputProfile, 3> INPUT_PROFILES{{
- {QT_TR_NOOP("Single Player - Handheld - Undocked"), HandheldOnProfileSelect,
- [](ConfigureInputSimple* caller) {
- CallConfigureDialog<ConfigureInputPlayer>(caller, HANDHELD_INDEX, false);
- }},
- {QT_TR_NOOP("Single Player - Dual Joycons - Docked"), DualJoyconsDockedOnProfileSelect,
- [](ConfigureInputSimple* caller) {
- CallConfigureDialog<ConfigureInputPlayer>(caller, PLAYER_0_INDEX, false);
- }},
- {QT_TR_NOOP("Custom"), [] {}, CallConfigureDialog<ConfigureInput>},
-}};
-
-} // namespace
-
-void ApplyInputProfileConfiguration(int profile_index) {
- std::get<1>(
- INPUT_PROFILES.at(std::min(profile_index, static_cast<int>(INPUT_PROFILES.size() - 1))))();
-}
-
-ConfigureInputSimple::ConfigureInputSimple(QWidget* parent)
- : QWidget(parent), ui(std::make_unique<Ui::ConfigureInputSimple>()) {
- ui->setupUi(this);
-
- for (const auto& profile : INPUT_PROFILES) {
- const QString label = tr(std::get<0>(profile));
- ui->profile_combobox->addItem(label, label);
- }
-
- connect(ui->profile_combobox, QOverload<int>::of(&QComboBox::currentIndexChanged), this,
- &ConfigureInputSimple::OnSelectProfile);
- connect(ui->profile_configure, &QPushButton::clicked, this, &ConfigureInputSimple::OnConfigure);
-
- LoadConfiguration();
-}
-
-ConfigureInputSimple::~ConfigureInputSimple() = default;
-
-void ConfigureInputSimple::ApplyConfiguration() {
- auto index = ui->profile_combobox->currentIndex();
- // Make the stored index for "Custom" very large so that if new profiles are added it
- // doesn't change.
- if (index >= static_cast<int>(INPUT_PROFILES.size() - 1)) {
- index = std::numeric_limits<int>::max();
- }
-
- UISettings::values.profile_index = index;
-}
-
-void ConfigureInputSimple::changeEvent(QEvent* event) {
- if (event->type() == QEvent::LanguageChange) {
- RetranslateUI();
- }
-
- QWidget::changeEvent(event);
-}
-
-void ConfigureInputSimple::RetranslateUI() {
- ui->retranslateUi(this);
-}
-
-void ConfigureInputSimple::LoadConfiguration() {
- const auto index = UISettings::values.profile_index;
- if (index >= static_cast<int>(INPUT_PROFILES.size()) || index < 0) {
- ui->profile_combobox->setCurrentIndex(static_cast<int>(INPUT_PROFILES.size() - 1));
- } else {
- ui->profile_combobox->setCurrentIndex(index);
- }
-}
-
-void ConfigureInputSimple::OnSelectProfile(int index) {
- const auto old_docked = Settings::values.use_docked_mode;
- ApplyInputProfileConfiguration(index);
- OnDockedModeChanged(old_docked, Settings::values.use_docked_mode);
-}
-
-void ConfigureInputSimple::OnConfigure() {
- std::get<2>(INPUT_PROFILES.at(ui->profile_combobox->currentIndex()))(this);
-}
diff --git a/src/yuzu/configuration/configure_input_simple.h b/src/yuzu/configuration/configure_input_simple.h
deleted file mode 100644
index bb5050224..000000000
--- a/src/yuzu/configuration/configure_input_simple.h
+++ /dev/null
@@ -1,43 +0,0 @@
-// Copyright 2016 Citra Emulator Project
-// Licensed under GPLv2 or any later version
-// Refer to the license.txt file included.
-
-#pragma once
-
-#include <memory>
-
-#include <QWidget>
-
-class QPushButton;
-class QString;
-class QTimer;
-
-namespace Ui {
-class ConfigureInputSimple;
-}
-
-// Used by configuration loader to apply a profile if the input is invalid.
-void ApplyInputProfileConfiguration(int profile_index);
-
-class ConfigureInputSimple : public QWidget {
- Q_OBJECT
-
-public:
- explicit ConfigureInputSimple(QWidget* parent = nullptr);
- ~ConfigureInputSimple() override;
-
- /// Save all button configurations to settings file
- void ApplyConfiguration();
-
-private:
- void changeEvent(QEvent* event) override;
- void RetranslateUI();
-
- /// Load configuration settings.
- void LoadConfiguration();
-
- void OnSelectProfile(int index);
- void OnConfigure();
-
- std::unique_ptr<Ui::ConfigureInputSimple> ui;
-};
diff --git a/src/yuzu/configuration/configure_input_simple.ui b/src/yuzu/configuration/configure_input_simple.ui
deleted file mode 100644
index c4889caa9..000000000
--- a/src/yuzu/configuration/configure_input_simple.ui
+++ /dev/null
@@ -1,97 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<ui version="4.0">
- <class>ConfigureInputSimple</class>
- <widget class="QWidget" name="ConfigureInputSimple">
- <property name="geometry">
- <rect>
- <x>0</x>
- <y>0</y>
- <width>473</width>
- <height>685</height>
- </rect>
- </property>
- <property name="windowTitle">
- <string>ConfigureInputSimple</string>
- </property>
- <layout class="QVBoxLayout" name="verticalLayout_5">
- <item>
- <layout class="QVBoxLayout" name="verticalLayout">
- <item>
- <widget class="QGroupBox" name="gridGroupBox">
- <property name="title">
- <string>Profile</string>
- </property>
- <layout class="QGridLayout" name="gridLayout">
- <item row="1" column="2">
- <widget class="QPushButton" name="profile_configure">
- <property name="text">
- <string>Configure</string>
- </property>
- </widget>
- </item>
- <item row="1" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="3">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>40</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="1" column="1">
- <widget class="QComboBox" name="profile_combobox">
- <property name="minimumSize">
- <size>
- <width>250</width>
- <height>0</height>
- </size>
- </property>
- </widget>
- </item>
- <item row="0" column="1" colspan="2">
- <widget class="QLabel" name="label">
- <property name="text">
- <string>Choose a controller configuration:</string>
- </property>
- </widget>
- </item>
- </layout>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <spacer name="verticalSpacer">
- <property name="orientation">
- <enum>Qt::Vertical</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>40</height>
- </size>
- </property>
- </spacer>
- </item>
- </layout>
- </widget>
- <resources/>
- <connections/>
-</ui>
diff --git a/src/yuzu/configuration/configure_motion_touch.cpp b/src/yuzu/configuration/configure_motion_touch.cpp
new file mode 100644
index 000000000..170574d9b
--- /dev/null
+++ b/src/yuzu/configuration/configure_motion_touch.cpp
@@ -0,0 +1,314 @@
+// Copyright 2018 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <array>
+#include <QCloseEvent>
+#include <QLabel>
+#include <QMessageBox>
+#include <QPushButton>
+#include <QVBoxLayout>
+#include "common/logging/log.h"
+#include "core/settings.h"
+#include "input_common/main.h"
+#include "input_common/udp/client.h"
+#include "input_common/udp/udp.h"
+#include "ui_configure_motion_touch.h"
+#include "yuzu/configuration/configure_motion_touch.h"
+#include "yuzu/configuration/configure_touch_from_button.h"
+
+CalibrationConfigurationDialog::CalibrationConfigurationDialog(QWidget* parent,
+ const std::string& host, u16 port,
+ u8 pad_index, u16 client_id)
+ : QDialog(parent) {
+ layout = new QVBoxLayout;
+ status_label = new QLabel(tr("Communicating with the server..."));
+ cancel_button = new QPushButton(tr("Cancel"));
+ connect(cancel_button, &QPushButton::clicked, this, [this] {
+ if (!completed) {
+ job->Stop();
+ }
+ accept();
+ });
+ layout->addWidget(status_label);
+ layout->addWidget(cancel_button);
+ setLayout(layout);
+
+ using namespace InputCommon::CemuhookUDP;
+ job = std::make_unique<CalibrationConfigurationJob>(
+ host, port, pad_index, client_id,
+ [this](CalibrationConfigurationJob::Status status) {
+ QString text;
+ switch (status) {
+ case CalibrationConfigurationJob::Status::Ready:
+ text = tr("Touch the top left corner <br>of your touchpad.");
+ break;
+ case CalibrationConfigurationJob::Status::Stage1Completed:
+ text = tr("Now touch the bottom right corner <br>of your touchpad.");
+ break;
+ case CalibrationConfigurationJob::Status::Completed:
+ text = tr("Configuration completed!");
+ break;
+ }
+ QMetaObject::invokeMethod(this, "UpdateLabelText", Q_ARG(QString, text));
+ if (status == CalibrationConfigurationJob::Status::Completed) {
+ QMetaObject::invokeMethod(this, "UpdateButtonText", Q_ARG(QString, tr("OK")));
+ }
+ },
+ [this](u16 min_x_, u16 min_y_, u16 max_x_, u16 max_y_) {
+ completed = true;
+ min_x = min_x_;
+ min_y = min_y_;
+ max_x = max_x_;
+ max_y = max_y_;
+ });
+}
+
+CalibrationConfigurationDialog::~CalibrationConfigurationDialog() = default;
+
+void CalibrationConfigurationDialog::UpdateLabelText(const QString& text) {
+ status_label->setText(text);
+}
+
+void CalibrationConfigurationDialog::UpdateButtonText(const QString& text) {
+ cancel_button->setText(text);
+}
+
+constexpr std::array<std::pair<const char*, const char*>, 2> MotionProviders = {{
+ {"motion_emu", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Mouse (Right Click)")},
+ {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
+}};
+
+constexpr std::array<std::pair<const char*, const char*>, 2> TouchProviders = {{
+ {"emu_window", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "Emulator Window")},
+ {"cemuhookudp", QT_TRANSLATE_NOOP("ConfigureMotionTouch", "CemuhookUDP")},
+}};
+
+ConfigureMotionTouch::ConfigureMotionTouch(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem_)
+ : QDialog(parent), input_subsystem{input_subsystem_},
+ ui(std::make_unique<Ui::ConfigureMotionTouch>()) {
+ ui->setupUi(this);
+ for (const auto& [provider, name] : MotionProviders) {
+ ui->motion_provider->addItem(tr(name), QString::fromUtf8(provider));
+ }
+ for (const auto& [provider, name] : TouchProviders) {
+ ui->touch_provider->addItem(tr(name), QString::fromUtf8(provider));
+ }
+
+ ui->udp_learn_more->setOpenExternalLinks(true);
+ ui->udp_learn_more->setText(
+ tr("<a "
+ "href='https://yuzu-emu.org/wiki/"
+ "using-a-controller-or-android-phone-for-motion-or-touch-input'><span "
+ "style=\"text-decoration: underline; color:#039be5;\">Learn More</span></a>"));
+
+ SetConfiguration();
+ UpdateUiDisplay();
+ ConnectEvents();
+}
+
+ConfigureMotionTouch::~ConfigureMotionTouch() = default;
+
+void ConfigureMotionTouch::SetConfiguration() {
+ const Common::ParamPackage motion_param(Settings::values.motion_device);
+ const Common::ParamPackage touch_param(Settings::values.touch_device);
+ const std::string motion_engine = motion_param.Get("engine", "motion_emu");
+ const std::string touch_engine = touch_param.Get("engine", "emu_window");
+
+ ui->motion_provider->setCurrentIndex(
+ ui->motion_provider->findData(QString::fromStdString(motion_engine)));
+ ui->touch_provider->setCurrentIndex(
+ ui->touch_provider->findData(QString::fromStdString(touch_engine)));
+ ui->touch_from_button_checkbox->setChecked(Settings::values.use_touch_from_button);
+ touch_from_button_maps = Settings::values.touch_from_button_maps;
+ for (const auto& touch_map : touch_from_button_maps) {
+ ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
+ }
+ ui->touch_from_button_map->setCurrentIndex(Settings::values.touch_from_button_map_index);
+ ui->motion_sensitivity->setValue(motion_param.Get("sensitivity", 0.01f));
+
+ min_x = touch_param.Get("min_x", 100);
+ min_y = touch_param.Get("min_y", 50);
+ max_x = touch_param.Get("max_x", 1800);
+ max_y = touch_param.Get("max_y", 850);
+
+ ui->udp_server->setText(QString::fromStdString(Settings::values.udp_input_address));
+ ui->udp_port->setText(QString::number(Settings::values.udp_input_port));
+ ui->udp_pad_index->setCurrentIndex(Settings::values.udp_pad_index);
+}
+
+void ConfigureMotionTouch::UpdateUiDisplay() {
+ const QString motion_engine = ui->motion_provider->currentData().toString();
+ const QString touch_engine = ui->touch_provider->currentData().toString();
+ const QString cemuhook_udp = QStringLiteral("cemuhookudp");
+
+ if (motion_engine == QStringLiteral("motion_emu")) {
+ ui->motion_sensitivity_label->setVisible(true);
+ ui->motion_sensitivity->setVisible(true);
+ } else {
+ ui->motion_sensitivity_label->setVisible(false);
+ ui->motion_sensitivity->setVisible(false);
+ }
+
+ if (touch_engine == cemuhook_udp) {
+ ui->touch_calibration->setVisible(true);
+ ui->touch_calibration_config->setVisible(true);
+ ui->touch_calibration_label->setVisible(true);
+ ui->touch_calibration->setText(
+ QStringLiteral("(%1, %2) - (%3, %4)").arg(min_x).arg(min_y).arg(max_x).arg(max_y));
+ } else {
+ ui->touch_calibration->setVisible(false);
+ ui->touch_calibration_config->setVisible(false);
+ ui->touch_calibration_label->setVisible(false);
+ }
+
+ if (motion_engine == cemuhook_udp || touch_engine == cemuhook_udp) {
+ ui->udp_config_group_box->setVisible(true);
+ } else {
+ ui->udp_config_group_box->setVisible(false);
+ }
+}
+
+void ConfigureMotionTouch::ConnectEvents() {
+ connect(ui->motion_provider, qOverload<int>(&QComboBox::currentIndexChanged), this,
+ [this](int index) { UpdateUiDisplay(); });
+ connect(ui->touch_provider, qOverload<int>(&QComboBox::currentIndexChanged), this,
+ [this](int index) { UpdateUiDisplay(); });
+ connect(ui->udp_test, &QPushButton::clicked, this, &ConfigureMotionTouch::OnCemuhookUDPTest);
+ connect(ui->touch_calibration_config, &QPushButton::clicked, this,
+ &ConfigureMotionTouch::OnConfigureTouchCalibration);
+ connect(ui->touch_from_button_config_btn, &QPushButton::clicked, this,
+ &ConfigureMotionTouch::OnConfigureTouchFromButton);
+ connect(ui->buttonBox, &QDialogButtonBox::rejected, this, [this] {
+ if (CanCloseDialog()) {
+ reject();
+ }
+ });
+}
+
+void ConfigureMotionTouch::OnCemuhookUDPTest() {
+ ui->udp_test->setEnabled(false);
+ ui->udp_test->setText(tr("Testing"));
+ udp_test_in_progress = true;
+ InputCommon::CemuhookUDP::TestCommunication(
+ ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toInt()),
+ static_cast<u32>(ui->udp_pad_index->currentIndex()), 24872,
+ [this] {
+ LOG_INFO(Frontend, "UDP input test success");
+ QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, true));
+ },
+ [this] {
+ LOG_ERROR(Frontend, "UDP input test failed");
+ QMetaObject::invokeMethod(this, "ShowUDPTestResult", Q_ARG(bool, false));
+ });
+}
+
+void ConfigureMotionTouch::OnConfigureTouchCalibration() {
+ ui->touch_calibration_config->setEnabled(false);
+ ui->touch_calibration_config->setText(tr("Configuring"));
+ CalibrationConfigurationDialog dialog(
+ this, ui->udp_server->text().toStdString(), static_cast<u16>(ui->udp_port->text().toUInt()),
+ static_cast<u8>(ui->udp_pad_index->currentIndex()), 24872);
+ dialog.exec();
+ if (dialog.completed) {
+ min_x = dialog.min_x;
+ min_y = dialog.min_y;
+ max_x = dialog.max_x;
+ max_y = dialog.max_y;
+ LOG_INFO(Frontend,
+ "UDP touchpad calibration config success: min_x={}, min_y={}, max_x={}, max_y={}",
+ min_x, min_y, max_x, max_y);
+ UpdateUiDisplay();
+ } else {
+ LOG_ERROR(Frontend, "UDP touchpad calibration config failed");
+ }
+ ui->touch_calibration_config->setEnabled(true);
+ ui->touch_calibration_config->setText(tr("Configure"));
+}
+
+void ConfigureMotionTouch::closeEvent(QCloseEvent* event) {
+ if (CanCloseDialog()) {
+ event->accept();
+ } else {
+ event->ignore();
+ }
+}
+
+void ConfigureMotionTouch::ShowUDPTestResult(bool result) {
+ udp_test_in_progress = false;
+ if (result) {
+ QMessageBox::information(this, tr("Test Successful"),
+ tr("Successfully received data from the server."));
+ } else {
+ QMessageBox::warning(this, tr("Test Failed"),
+ tr("Could not receive valid data from the server.<br>Please verify "
+ "that the server is set up correctly and "
+ "the address and port are correct."));
+ }
+ ui->udp_test->setEnabled(true);
+ ui->udp_test->setText(tr("Test"));
+}
+
+void ConfigureMotionTouch::OnConfigureTouchFromButton() {
+ ConfigureTouchFromButton dialog{this, touch_from_button_maps, input_subsystem,
+ ui->touch_from_button_map->currentIndex()};
+ if (dialog.exec() != QDialog::Accepted) {
+ return;
+ }
+ touch_from_button_maps = dialog.GetMaps();
+
+ while (ui->touch_from_button_map->count() > 0) {
+ ui->touch_from_button_map->removeItem(0);
+ }
+ for (const auto& touch_map : touch_from_button_maps) {
+ ui->touch_from_button_map->addItem(QString::fromStdString(touch_map.name));
+ }
+ ui->touch_from_button_map->setCurrentIndex(dialog.GetSelectedIndex());
+}
+
+bool ConfigureMotionTouch::CanCloseDialog() {
+ if (udp_test_in_progress) {
+ QMessageBox::warning(this, tr("Citra"),
+ tr("UDP Test or calibration configuration is in progress.<br>Please "
+ "wait for them to finish."));
+ return false;
+ }
+ return true;
+}
+
+void ConfigureMotionTouch::ApplyConfiguration() {
+ if (!CanCloseDialog()) {
+ return;
+ }
+
+ std::string motion_engine = ui->motion_provider->currentData().toString().toStdString();
+ std::string touch_engine = ui->touch_provider->currentData().toString().toStdString();
+
+ Common::ParamPackage motion_param{}, touch_param{};
+ motion_param.Set("engine", std::move(motion_engine));
+ touch_param.Set("engine", std::move(touch_engine));
+
+ if (motion_engine == "motion_emu") {
+ motion_param.Set("sensitivity", static_cast<float>(ui->motion_sensitivity->value()));
+ }
+
+ if (touch_engine == "cemuhookudp") {
+ touch_param.Set("min_x", min_x);
+ touch_param.Set("min_y", min_y);
+ touch_param.Set("max_x", max_x);
+ touch_param.Set("max_y", max_y);
+ }
+
+ Settings::values.motion_device = motion_param.Serialize();
+ Settings::values.touch_device = touch_param.Serialize();
+ Settings::values.use_touch_from_button = ui->touch_from_button_checkbox->isChecked();
+ Settings::values.touch_from_button_map_index = ui->touch_from_button_map->currentIndex();
+ Settings::values.touch_from_button_maps = touch_from_button_maps;
+ Settings::values.udp_input_address = ui->udp_server->text().toStdString();
+ Settings::values.udp_input_port = static_cast<u16>(ui->udp_port->text().toInt());
+ Settings::values.udp_pad_index = static_cast<u8>(ui->udp_pad_index->currentIndex());
+ input_subsystem->ReloadInputDevices();
+
+ accept();
+}
diff --git a/src/yuzu/configuration/configure_motion_touch.h b/src/yuzu/configuration/configure_motion_touch.h
new file mode 100644
index 000000000..3d4b5d659
--- /dev/null
+++ b/src/yuzu/configuration/configure_motion_touch.h
@@ -0,0 +1,90 @@
+// Copyright 2018 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <memory>
+#include <QDialog>
+#include "common/param_package.h"
+
+class QLabel;
+class QPushButton;
+class QVBoxLayout;
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
+namespace InputCommon::CemuhookUDP {
+class CalibrationConfigurationJob;
+}
+
+namespace Ui {
+class ConfigureMotionTouch;
+}
+
+/// A dialog for touchpad calibration configuration.
+class CalibrationConfigurationDialog : public QDialog {
+ Q_OBJECT
+public:
+ explicit CalibrationConfigurationDialog(QWidget* parent, const std::string& host, u16 port,
+ u8 pad_index, u16 client_id);
+ ~CalibrationConfigurationDialog() override;
+
+private:
+ Q_INVOKABLE void UpdateLabelText(const QString& text);
+ Q_INVOKABLE void UpdateButtonText(const QString& text);
+
+ QVBoxLayout* layout;
+ QLabel* status_label;
+ QPushButton* cancel_button;
+ std::unique_ptr<InputCommon::CemuhookUDP::CalibrationConfigurationJob> job;
+
+ // Configuration results
+ bool completed{};
+ u16 min_x{};
+ u16 min_y{};
+ u16 max_x{};
+ u16 max_y{};
+
+ friend class ConfigureMotionTouch;
+};
+
+class ConfigureMotionTouch : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureMotionTouch(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
+ ~ConfigureMotionTouch() override;
+
+public slots:
+ void ApplyConfiguration();
+
+private slots:
+ void OnCemuhookUDPTest();
+ void OnConfigureTouchCalibration();
+ void OnConfigureTouchFromButton();
+
+private:
+ void closeEvent(QCloseEvent* event) override;
+ Q_INVOKABLE void ShowUDPTestResult(bool result);
+ void SetConfiguration();
+ void UpdateUiDisplay();
+ void ConnectEvents();
+ bool CanCloseDialog();
+
+ InputCommon::InputSubsystem* input_subsystem;
+
+ std::unique_ptr<Ui::ConfigureMotionTouch> ui;
+
+ // Coordinate system of the CemuhookUDP touch provider
+ int min_x{};
+ int min_y{};
+ int max_x{};
+ int max_y{};
+
+ bool udp_test_in_progress{};
+
+ std::vector<Settings::TouchFromButtonMap> touch_from_button_maps;
+};
diff --git a/src/yuzu/configuration/configure_motion_touch.ui b/src/yuzu/configuration/configure_motion_touch.ui
new file mode 100644
index 000000000..5b78c5a4b
--- /dev/null
+++ b/src/yuzu/configuration/configure_motion_touch.ui
@@ -0,0 +1,317 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureMotionTouch</class>
+ <widget class="QDialog" name="ConfigureMotionTouch">
+ <property name="windowTitle">
+ <string>Configure Motion / Touch</string>
+ </property>
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>500</width>
+ <height>450</height>
+ </rect>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="motion_group_box">
+ <property name="title">
+ <string>Motion</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="motion_provider_label">
+ <property name="text">
+ <string>Motion Provider:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="motion_provider"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="motion_sensitivity_label">
+ <property name="text">
+ <string>Sensitivity:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDoubleSpinBox" name="motion_sensitivity">
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ <property name="decimals">
+ <number>4</number>
+ </property>
+ <property name="minimum">
+ <double>0.010000000000000</double>
+ </property>
+ <property name="maximum">
+ <double>10.000000000000000</double>
+ </property>
+ <property name="singleStep">
+ <double>0.001000000000000</double>
+ </property>
+ <property name="value">
+ <double>0.010000000000000</double>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="touch_group_box">
+ <property name="title">
+ <string>Touch</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="touch_provider_label">
+ <property name="text">
+ <string>Touch Provider:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="touch_provider"/>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="touch_calibration_label">
+ <property name="text">
+ <string>Calibration:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLabel" name="touch_calibration">
+ <property name="text">
+ <string>(100, 50) - (1800, 850)</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter</set>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="touch_calibration_config">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QCheckBox" name="touch_from_button_checkbox">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Use button mapping:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="touch_from_button_map"/>
+ </item>
+ <item>
+ <widget class="QPushButton" name="touch_from_button_config_btn">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Configure</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="udp_config_group_box">
+ <property name="title">
+ <string>CemuhookUDP Config</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QLabel" name="udp_help">
+ <property name="text">
+ <string>You may use any Cemuhook compatible UDP input source to provide motion and touch input.</string>
+ </property>
+ <property name="alignment">
+ <set>Qt::AlignLeading|Qt::AlignLeft|Qt::AlignTop</set>
+ </property>
+ <property name="wordWrap">
+ <bool>true</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="udp_server_label">
+ <property name="text">
+ <string>Server:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="udp_server">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="udp_port_label">
+ <property name="text">
+ <string>Port:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QLineEdit" name="udp_port">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Minimum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="udp_pad_index_label">
+ <property name="text">
+ <string>Pad:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="udp_pad_index">
+ <item>
+ <property name="text">
+ <string>Pad 1</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pad 2</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pad 3</string>
+ </property>
+ </item>
+ <item>
+ <property name="text">
+ <string>Pad 4</string>
+ </property>
+ </item>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <layout class="QHBoxLayout">
+ <item>
+ <widget class="QLabel" name="udp_learn_more">
+ <property name="text">
+ <string>Learn More</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="udp_test">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Test</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer>
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>167</width>
+ <height>55</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureMotionTouch</receiver>
+ <slot>ApplyConfiguration()</slot>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/configuration/configure_mouse_advanced.cpp b/src/yuzu/configuration/configure_mouse_advanced.cpp
index 5bcf5ffa8..2af3afda8 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.cpp
+++ b/src/yuzu/configuration/configure_mouse_advanced.cpp
@@ -18,6 +18,16 @@
static QString GetKeyName(int key_code) {
switch (key_code) {
+ case Qt::LeftButton:
+ return QObject::tr("Click 0");
+ case Qt::RightButton:
+ return QObject::tr("Click 1");
+ case Qt::MiddleButton:
+ return QObject::tr("Click 2");
+ case Qt::BackButton:
+ return QObject::tr("Click 3");
+ case Qt::ForwardButton:
+ return QObject::tr("Click 4");
case Qt::Key_Shift:
return QObject::tr("Shift");
case Qt::Key_Control:
@@ -66,8 +76,10 @@ static QString ButtonToText(const Common::ParamPackage& param) {
return QObject::tr("[unknown]");
}
-ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent)
- : QDialog(parent), ui(std::make_unique<Ui::ConfigureMouseAdvanced>()),
+ConfigureMouseAdvanced::ConfigureMouseAdvanced(QWidget* parent,
+ InputCommon::InputSubsystem* input_subsystem_)
+ : QDialog(parent),
+ ui(std::make_unique<Ui::ConfigureMouseAdvanced>()), input_subsystem{input_subsystem_},
timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
@@ -188,9 +200,9 @@ void ConfigureMouseAdvanced::HandleClick(
button->setText(tr("[press key]"));
button->setFocus();
- // Keyboard keys can only be used as button devices
- want_keyboard_keys = type == InputCommon::Polling::DeviceType::Button;
- if (want_keyboard_keys) {
+ // Keyboard keys or mouse buttons can only be used as button devices
+ want_keyboard_mouse = type == InputCommon::Polling::DeviceType::Button;
+ if (want_keyboard_mouse) {
const auto iter = std::find(button_map.begin(), button_map.end(), button);
ASSERT(iter != button_map.end());
const auto index = std::distance(button_map.begin(), iter);
@@ -199,27 +211,29 @@ void ConfigureMouseAdvanced::HandleClick(
input_setter = new_input_setter;
- device_pollers = InputCommon::Polling::GetPollers(type);
+ device_pollers = input_subsystem->GetPollers(type);
for (auto& poller : device_pollers) {
poller->Start();
}
- grabKeyboard();
- grabMouse();
- timeout_timer->start(5000); // Cancel after 5 seconds
- poll_timer->start(200); // Check for new inputs every 200ms
+ QWidget::grabMouse();
+ QWidget::grabKeyboard();
+
+ timeout_timer->start(2500); // Cancel after 2.5 seconds
+ poll_timer->start(50); // Check for new inputs every 50ms
}
void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params, bool abort) {
- releaseKeyboard();
- releaseMouse();
timeout_timer->stop();
poll_timer->stop();
for (auto& poller : device_pollers) {
poller->Stop();
}
+ QWidget::releaseMouse();
+ QWidget::releaseKeyboard();
+
if (!abort) {
(*input_setter)(params);
}
@@ -228,13 +242,29 @@ void ConfigureMouseAdvanced::SetPollingResult(const Common::ParamPackage& params
input_setter = std::nullopt;
}
+void ConfigureMouseAdvanced::mousePressEvent(QMouseEvent* event) {
+ if (!input_setter || !event) {
+ return;
+ }
+
+ if (want_keyboard_mouse) {
+ SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->button())},
+ false);
+ } else {
+ // We don't want any mouse buttons, so don't stop polling
+ return;
+ }
+
+ SetPollingResult({}, true);
+}
+
void ConfigureMouseAdvanced::keyPressEvent(QKeyEvent* event) {
if (!input_setter || !event) {
return;
}
if (event->key() != Qt::Key_Escape) {
- if (want_keyboard_keys) {
+ if (want_keyboard_mouse) {
SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
false);
} else {
diff --git a/src/yuzu/configuration/configure_mouse_advanced.h b/src/yuzu/configuration/configure_mouse_advanced.h
index 342b82412..65b6fca9a 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.h
+++ b/src/yuzu/configuration/configure_mouse_advanced.h
@@ -8,12 +8,14 @@
#include <optional>
#include <QDialog>
-#include "core/settings.h"
-
class QCheckBox;
class QPushButton;
class QTimer;
+namespace InputCommon {
+class InputSubsystem;
+}
+
namespace Ui {
class ConfigureMouseAdvanced;
}
@@ -22,7 +24,7 @@ class ConfigureMouseAdvanced : public QDialog {
Q_OBJECT
public:
- explicit ConfigureMouseAdvanced(QWidget* parent);
+ explicit ConfigureMouseAdvanced(QWidget* parent, InputCommon::InputSubsystem* input_subsystem_);
~ConfigureMouseAdvanced() override;
void ApplyConfiguration();
@@ -49,11 +51,16 @@ private:
/// Finish polling and configure input using the input_setter
void SetPollingResult(const Common::ParamPackage& params, bool abort);
+ /// Handle mouse button press events.
+ void mousePressEvent(QMouseEvent* event) override;
+
/// Handle key press events.
void keyPressEvent(QKeyEvent* event) override;
std::unique_ptr<Ui::ConfigureMouseAdvanced> ui;
+ InputCommon::InputSubsystem* input_subsystem;
+
/// This will be the the setting function when an input is awaiting configuration.
std::optional<std::function<void(const Common::ParamPackage&)>> input_setter;
@@ -67,5 +74,5 @@ private:
/// A flag to indicate if keyboard keys are okay when configuring an input. If this is false,
/// keyboard events are ignored.
- bool want_keyboard_keys = false;
+ bool want_keyboard_mouse = false;
};
diff --git a/src/yuzu/configuration/configure_mouse_advanced.ui b/src/yuzu/configuration/configure_mouse_advanced.ui
index 08245ecf0..5b99e1c37 100644
--- a/src/yuzu/configuration/configure_mouse_advanced.ui
+++ b/src/yuzu/configuration/configure_mouse_advanced.ui
@@ -6,13 +6,18 @@
<rect>
<x>0</x>
<y>0</y>
- <width>250</width>
- <height>261</height>
+ <width>310</width>
+ <height>193</height>
</rect>
</property>
<property name="windowTitle">
<string>Configure Mouse</string>
</property>
+ <property name="styleSheet">
+ <string notr="true">QPushButton {
+ min-width: 60px;
+}</string>
+ </property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QGroupBox" name="gridGroupBox">
@@ -20,81 +25,33 @@
<string>Mouse Buttons</string>
</property>
<layout class="QGridLayout" name="gridLayout">
- <item row="0" column="4">
- <spacer name="horizontalSpacer_2">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="0" column="3">
- <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item row="3" column="5">
+ <layout class="QVBoxLayout" name="verticalLayout_6">
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <layout class="QHBoxLayout" name="horizontalLayout_5">
<item>
- <widget class="QLabel" name="label_3">
+ <widget class="QLabel" name="label_5">
<property name="text">
- <string>Right:</string>
+ <string>Forward:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
- <widget class="QPushButton" name="right_button">
+ <widget class="QPushButton" name="forward_button">
<property name="minimumSize">
<size>
- <width>75</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
- <property name="text">
- <string/>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
</property>
- </widget>
- </item>
- </layout>
- </item>
- <item row="0" column="0">
- <spacer name="horizontalSpacer">
- <property name="orientation">
- <enum>Qt::Horizontal</enum>
- </property>
- <property name="sizeType">
- <enum>QSizePolicy::Fixed</enum>
- </property>
- <property name="sizeHint" stdset="0">
- <size>
- <width>20</width>
- <height>20</height>
- </size>
- </property>
- </spacer>
- </item>
- <item row="2" column="1">
- <layout class="QVBoxLayout" name="verticalLayout_3">
- <item>
- <layout class="QHBoxLayout" name="horizontalLayout_2">
- <item>
- <widget class="QLabel" name="label_2">
- <property name="text">
- <string>Middle:</string>
- </property>
- </widget>
- </item>
- </layout>
- </item>
- <item>
- <widget class="QPushButton" name="middle_button">
<property name="text">
<string/>
</property>
@@ -123,6 +80,12 @@
</item>
<item>
<widget class="QPushButton" name="back_button">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
<property name="text">
<string/>
</property>
@@ -147,7 +110,7 @@
<widget class="QPushButton" name="left_button">
<property name="minimumSize">
<size>
- <width>75</width>
+ <width>68</width>
<height>0</height>
</size>
</property>
@@ -158,21 +121,99 @@
</item>
</layout>
</item>
- <item row="3" column="3">
- <layout class="QVBoxLayout" name="verticalLayout_6">
+ <item row="0" column="3">
+ <layout class="QVBoxLayout" name="verticalLayout_3">
<item>
- <layout class="QHBoxLayout" name="horizontalLayout_5">
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
<item>
- <widget class="QLabel" name="label_5">
+ <widget class="QLabel" name="label_2">
<property name="text">
- <string>Forward:</string>
+ <string>Middle:</string>
</property>
</widget>
</item>
</layout>
</item>
<item>
- <widget class="QPushButton" name="forward_button">
+ <widget class="QPushButton" name="middle_button">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="text">
+ <string/>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item row="0" column="6">
+ <spacer name="horizontalSpacer_2">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="0">
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeType">
+ <enum>QSizePolicy::Fixed</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="5">
+ <layout class="QVBoxLayout" name="verticalLayout_4">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="label_3">
+ <property name="text">
+ <string>Right:</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QPushButton" name="right_button">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
<property name="text">
<string/>
</property>
@@ -180,6 +221,32 @@
</item>
</layout>
</item>
+ <item row="0" column="2">
+ <spacer name="horizontalSpacer_4">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item row="0" column="4">
+ <spacer name="horizontalSpacer_5">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>0</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
</layout>
</widget>
</item>
@@ -187,15 +254,39 @@
<layout class="QHBoxLayout" name="horizontalLayout_6">
<item>
<widget class="QPushButton" name="buttonClearAll">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
<property name="text">
- <string>Clear All</string>
+ <string>Clear</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="buttonRestoreDefaults">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
<property name="text">
- <string>Restore Defaults</string>
+ <string>Defaults</string>
</property>
</widget>
</item>
@@ -206,21 +297,24 @@
</property>
<property name="sizeHint" stdset="0">
<size>
- <width>40</width>
+ <width>0</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
</layout>
</item>
- <item>
- <widget class="QDialogButtonBox" name="buttonBox">
- <property name="standardButtons">
- <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
- </property>
- </widget>
- </item>
</layout>
</widget>
<resources/>
@@ -230,32 +324,12 @@
<signal>accepted()</signal>
<receiver>ConfigureMouseAdvanced</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>124</x>
- <y>266</y>
- </hint>
- <hint type="destinationlabel">
- <x>124</x>
- <y>143</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureMouseAdvanced</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>124</x>
- <y>266</y>
- </hint>
- <hint type="destinationlabel">
- <x>124</x>
- <y>143</y>
- </hint>
- </hints>
</connection>
</connections>
</ui>
diff --git a/src/yuzu/configuration/configure_per_game.cpp b/src/yuzu/configuration/configure_per_game.cpp
index 1e49f0787..8eac3bd9d 100644
--- a/src/yuzu/configuration/configure_per_game.cpp
+++ b/src/yuzu/configuration/configure_per_game.cpp
@@ -16,6 +16,7 @@
#include "common/common_paths.h"
#include "common/file_util.h"
+#include "core/core.h"
#include "core/file_sys/control_metadata.h"
#include "core/file_sys/patch_manager.h"
#include "core/file_sys/xts_archive.h"
@@ -29,9 +30,10 @@
ConfigurePerGame::ConfigurePerGame(QWidget* parent, u64 title_id)
: QDialog(parent), ui(std::make_unique<Ui::ConfigurePerGame>()), title_id(title_id) {
- game_config = std::make_unique<Config>(fmt::format("{:016X}.ini", title_id), false);
+ game_config = std::make_unique<Config>(fmt::format("{:016X}", title_id),
+ Config::ConfigType::PerGameConfig);
- Settings::configuring_global = false;
+ Settings::SetConfiguringGlobal(false);
ui->setupUi(this);
setFocusPolicy(Qt::ClickFocus);
@@ -88,9 +90,11 @@ void ConfigurePerGame::LoadConfiguration() {
ui->display_title_id->setText(
QStringLiteral("%1").arg(title_id, 16, 16, QLatin1Char{'0'}).toUpper());
- FileSys::PatchManager pm{title_id};
+ auto& system = Core::System::GetInstance();
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto control = pm.GetControlMetadata();
- const auto loader = Loader::GetLoader(file);
+ const auto loader = Loader::GetLoader(system, file);
if (control.first != nullptr) {
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 @@
<signal>accepted()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>248</x>
- <y>254</y>
- </hint>
- <hint type="destinationlabel">
- <x>157</x>
- <y>274</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigurePerGame</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>316</x>
- <y>260</y>
- </hint>
- <hint type="destinationlabel">
- <x>286</x>
- <y>274</y>
- </hint>
- </hints>
</connection>
</connections>
</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() {
return;
}
- FileSys::PatchManager pm{title_id};
- const auto loader = Loader::GetLoader(file);
+ auto& system = Core::System::GetInstance();
+ const FileSys::PatchManager pm{title_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
+ const auto loader = Loader::GetLoader(system, file);
FileSys::VirtualFile update_raw;
loader->ReadUpdateRaw(update_raw);
diff --git a/src/yuzu/configuration/configure_system.cpp b/src/yuzu/configuration/configure_system.cpp
index 9ad43ed8f..59a58d92c 100644
--- a/src/yuzu/configuration/configure_system.cpp
+++ b/src/yuzu/configuration/configure_system.cpp
@@ -12,6 +12,7 @@
#include "common/assert.h"
#include "common/file_util.h"
#include "core/core.h"
+#include "core/hle/service/time/time.h"
#include "core/settings.h"
#include "ui_configure_system.h"
#include "yuzu/configuration/configuration_shared.h"
@@ -36,8 +37,8 @@ ConfigureSystem::ConfigureSystem(QWidget* parent) : QWidget(parent), ui(new Ui::
}
});
- ui->label_console_id->setVisible(Settings::configuring_global);
- ui->button_regenerate_console_id->setVisible(Settings::configuring_global);
+ ui->label_console_id->setVisible(Settings::IsConfiguringGlobal());
+ ui->button_regenerate_console_id->setVisible(Settings::IsConfiguringGlobal());
SetupPerGameUI();
@@ -77,7 +78,7 @@ void ConfigureSystem::SetConfiguration() {
Settings::values.rng_seed.UsingGlobal());
ui->custom_rtc_edit->setDateTime(QDateTime::fromSecsSinceEpoch(rtc_time.count()));
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->combo_language->setCurrentIndex(Settings::values.language_index.GetValue());
ui->combo_region->setCurrentIndex(Settings::values.region_index.GetValue());
ui->combo_time_zone->setCurrentIndex(Settings::values.time_zone_index.GetValue());
@@ -104,11 +105,27 @@ void ConfigureSystem::SetConfiguration() {
void ConfigureSystem::ReadSystemSettings() {}
void ConfigureSystem::ApplyConfiguration() {
+ // Allow setting custom RTC even if system is powered on, to allow in-game time to be fast
+ // forwared
+ if (Settings::values.custom_rtc.UsingGlobal()) {
+ if (ui->custom_rtc_checkbox->isChecked()) {
+ Settings::values.custom_rtc.SetValue(
+ std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
+ if (Core::System::GetInstance().IsPoweredOn()) {
+ const s64 posix_time{Settings::values.custom_rtc.GetValue()->count() +
+ Service::Time::TimeManager::GetExternalTimeZoneOffset()};
+ Core::System::GetInstance().GetTimeManager().UpdateLocalSystemClockTime(posix_time);
+ }
+ } else {
+ Settings::values.custom_rtc.SetValue(std::nullopt);
+ }
+ }
+
if (!enabled) {
return;
}
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
// Guard if during game and set to game-specific value
if (Settings::values.language_index.UsingGlobal()) {
Settings::values.language_index.SetValue(ui->combo_language->currentIndex());
@@ -131,15 +148,6 @@ void ConfigureSystem::ApplyConfiguration() {
Settings::values.rng_seed.SetValue(std::nullopt);
}
}
-
- if (Settings::values.custom_rtc.UsingGlobal()) {
- if (ui->custom_rtc_checkbox->isChecked()) {
- Settings::values.custom_rtc.SetValue(
- std::chrono::seconds(ui->custom_rtc_edit->dateTime().toSecsSinceEpoch()));
- } else {
- Settings::values.custom_rtc.SetValue(std::nullopt);
- }
- }
} else {
ConfigurationShared::ApplyPerGameSetting(&Settings::values.language_index,
ui->combo_language);
@@ -210,7 +218,7 @@ void ConfigureSystem::RefreshConsoleID() {
}
void ConfigureSystem::SetupPerGameUI() {
- if (Settings::configuring_global) {
+ if (Settings::IsConfiguringGlobal()) {
ui->combo_language->setEnabled(Settings::values.language_index.UsingGlobal());
ui->combo_region->setEnabled(Settings::values.region_index.UsingGlobal());
ui->combo_time_zone->setEnabled(Settings::values.time_zone_index.UsingGlobal());
diff --git a/src/yuzu/configuration/configure_touch_from_button.cpp b/src/yuzu/configuration/configure_touch_from_button.cpp
new file mode 100644
index 000000000..15557e4b8
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_from_button.cpp
@@ -0,0 +1,623 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <QInputDialog>
+#include <QKeyEvent>
+#include <QMessageBox>
+#include <QMouseEvent>
+#include <QResizeEvent>
+#include <QStandardItemModel>
+#include <QTimer>
+#include "common/param_package.h"
+#include "core/frontend/framebuffer_layout.h"
+#include "core/settings.h"
+#include "input_common/main.h"
+#include "ui_configure_touch_from_button.h"
+#include "yuzu/configuration/configure_touch_from_button.h"
+#include "yuzu/configuration/configure_touch_widget.h"
+
+static QString GetKeyName(int key_code) {
+ switch (key_code) {
+ case Qt::Key_Shift:
+ return QObject::tr("Shift");
+ case Qt::Key_Control:
+ return QObject::tr("Ctrl");
+ case Qt::Key_Alt:
+ return QObject::tr("Alt");
+ case Qt::Key_Meta:
+ return QString{};
+ default:
+ return QKeySequence(key_code).toString();
+ }
+}
+
+static QString ButtonToText(const Common::ParamPackage& param) {
+ if (!param.Has("engine")) {
+ return QObject::tr("[not set]");
+ }
+
+ if (param.Get("engine", "") == "keyboard") {
+ return GetKeyName(param.Get("code", 0));
+ }
+
+ if (param.Get("engine", "") == "sdl") {
+ if (param.Has("hat")) {
+ const QString hat_str = QString::fromStdString(param.Get("hat", ""));
+ const QString direction_str = QString::fromStdString(param.Get("direction", ""));
+
+ return QObject::tr("Hat %1 %2").arg(hat_str, direction_str);
+ }
+
+ if (param.Has("axis")) {
+ const QString axis_str = QString::fromStdString(param.Get("axis", ""));
+ const QString direction_str = QString::fromStdString(param.Get("direction", ""));
+
+ return QObject::tr("Axis %1%2").arg(axis_str, direction_str);
+ }
+
+ if (param.Has("button")) {
+ const QString button_str = QString::fromStdString(param.Get("button", ""));
+
+ return QObject::tr("Button %1").arg(button_str);
+ }
+
+ return {};
+ }
+
+ return QObject::tr("[unknown]");
+}
+
+ConfigureTouchFromButton::ConfigureTouchFromButton(
+ QWidget* parent, const std::vector<Settings::TouchFromButtonMap>& touch_maps,
+ InputCommon::InputSubsystem* input_subsystem_, const int default_index)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureTouchFromButton>()),
+ touch_maps(touch_maps), input_subsystem{input_subsystem_}, selected_index(default_index),
+ timeout_timer(std::make_unique<QTimer>()), poll_timer(std::make_unique<QTimer>()) {
+ ui->setupUi(this);
+ binding_list_model = new QStandardItemModel(0, 3, this);
+ binding_list_model->setHorizontalHeaderLabels(
+ {tr("Button"), tr("X", "X axis"), tr("Y", "Y axis")});
+ ui->binding_list->setModel(binding_list_model);
+ ui->bottom_screen->SetCoordLabel(ui->coord_label);
+
+ SetConfiguration();
+ UpdateUiDisplay();
+ ConnectEvents();
+}
+
+ConfigureTouchFromButton::~ConfigureTouchFromButton() = default;
+
+void ConfigureTouchFromButton::showEvent(QShowEvent* ev) {
+ QWidget::showEvent(ev);
+
+ // width values are not valid in the constructor
+ const int w =
+ ui->binding_list->viewport()->contentsRect().width() / binding_list_model->columnCount();
+ if (w <= 0) {
+ return;
+ }
+ ui->binding_list->setColumnWidth(0, w);
+ ui->binding_list->setColumnWidth(1, w);
+ ui->binding_list->setColumnWidth(2, w);
+}
+
+void ConfigureTouchFromButton::SetConfiguration() {
+ for (const auto& touch_map : touch_maps) {
+ ui->mapping->addItem(QString::fromStdString(touch_map.name));
+ }
+
+ ui->mapping->setCurrentIndex(selected_index);
+}
+
+void ConfigureTouchFromButton::UpdateUiDisplay() {
+ ui->button_delete->setEnabled(touch_maps.size() > 1);
+ ui->button_delete_bind->setEnabled(false);
+
+ binding_list_model->removeRows(0, binding_list_model->rowCount());
+
+ for (const auto& button_str : touch_maps[selected_index].buttons) {
+ Common::ParamPackage package{button_str};
+ QStandardItem* button = new QStandardItem(ButtonToText(package));
+ button->setData(QString::fromStdString(button_str));
+ button->setEditable(false);
+ QStandardItem* xcoord = new QStandardItem(QString::number(package.Get("x", 0)));
+ QStandardItem* ycoord = new QStandardItem(QString::number(package.Get("y", 0)));
+ binding_list_model->appendRow({button, xcoord, ycoord});
+
+ const int dot = ui->bottom_screen->AddDot(package.Get("x", 0), package.Get("y", 0));
+ button->setData(dot, DataRoleDot);
+ }
+}
+
+void ConfigureTouchFromButton::ConnectEvents() {
+ connect(ui->mapping, qOverload<int>(&QComboBox::currentIndexChanged), this, [this](int index) {
+ SaveCurrentMapping();
+ selected_index = index;
+ UpdateUiDisplay();
+ });
+ connect(ui->button_new, &QPushButton::clicked, this, &ConfigureTouchFromButton::NewMapping);
+ connect(ui->button_delete, &QPushButton::clicked, this,
+ &ConfigureTouchFromButton::DeleteMapping);
+ connect(ui->button_rename, &QPushButton::clicked, this,
+ &ConfigureTouchFromButton::RenameMapping);
+ connect(ui->button_delete_bind, &QPushButton::clicked, this,
+ &ConfigureTouchFromButton::DeleteBinding);
+ connect(ui->binding_list, &QTreeView::doubleClicked, this,
+ &ConfigureTouchFromButton::EditBinding);
+ connect(ui->binding_list->selectionModel(), &QItemSelectionModel::selectionChanged, this,
+ &ConfigureTouchFromButton::OnBindingSelection);
+ connect(binding_list_model, &QStandardItemModel::itemChanged, this,
+ &ConfigureTouchFromButton::OnBindingChanged);
+ connect(ui->binding_list->model(), &QStandardItemModel::rowsAboutToBeRemoved, this,
+ &ConfigureTouchFromButton::OnBindingDeleted);
+ connect(ui->bottom_screen, &TouchScreenPreview::DotAdded, this,
+ &ConfigureTouchFromButton::NewBinding);
+ connect(ui->bottom_screen, &TouchScreenPreview::DotSelected, this,
+ &ConfigureTouchFromButton::SetActiveBinding);
+ connect(ui->bottom_screen, &TouchScreenPreview::DotMoved, this,
+ &ConfigureTouchFromButton::SetCoordinates);
+ connect(ui->buttonBox, &QDialogButtonBox::accepted, this,
+ &ConfigureTouchFromButton::ApplyConfiguration);
+
+ connect(timeout_timer.get(), &QTimer::timeout, [this]() { SetPollingResult({}, true); });
+
+ connect(poll_timer.get(), &QTimer::timeout, [this]() {
+ Common::ParamPackage params;
+ for (auto& poller : device_pollers) {
+ params = poller->GetNextInput();
+ if (params.Has("engine")) {
+ SetPollingResult(params, false);
+ return;
+ }
+ }
+ });
+}
+
+void ConfigureTouchFromButton::SaveCurrentMapping() {
+ auto& map = touch_maps[selected_index];
+ map.buttons.clear();
+ for (int i = 0, rc = binding_list_model->rowCount(); i < rc; ++i) {
+ const auto bind_str = binding_list_model->index(i, 0)
+ .data(Qt::ItemDataRole::UserRole + 1)
+ .toString()
+ .toStdString();
+ if (bind_str.empty()) {
+ continue;
+ }
+ Common::ParamPackage params{bind_str};
+ if (!params.Has("engine")) {
+ continue;
+ }
+ params.Set("x", binding_list_model->index(i, 1).data().toInt());
+ params.Set("y", binding_list_model->index(i, 2).data().toInt());
+ map.buttons.emplace_back(params.Serialize());
+ }
+}
+
+void ConfigureTouchFromButton::NewMapping() {
+ const QString name =
+ QInputDialog::getText(this, tr("New Profile"), tr("Enter the name for the new profile."));
+ if (name.isEmpty()) {
+ return;
+ }
+ touch_maps.emplace_back(Settings::TouchFromButtonMap{name.toStdString(), {}});
+ ui->mapping->addItem(name);
+ ui->mapping->setCurrentIndex(ui->mapping->count() - 1);
+}
+
+void ConfigureTouchFromButton::DeleteMapping() {
+ const auto answer = QMessageBox::question(
+ this, tr("Delete Profile"), tr("Delete profile %1?").arg(ui->mapping->currentText()));
+ if (answer != QMessageBox::Yes) {
+ return;
+ }
+ const bool blocked = ui->mapping->blockSignals(true);
+ ui->mapping->removeItem(selected_index);
+ ui->mapping->blockSignals(blocked);
+ touch_maps.erase(touch_maps.begin() + selected_index);
+ selected_index = ui->mapping->currentIndex();
+ UpdateUiDisplay();
+}
+
+void ConfigureTouchFromButton::RenameMapping() {
+ const QString new_name = QInputDialog::getText(this, tr("Rename Profile"), tr("New name:"));
+ if (new_name.isEmpty()) {
+ return;
+ }
+ ui->mapping->setItemText(selected_index, new_name);
+ touch_maps[selected_index].name = new_name.toStdString();
+}
+
+void ConfigureTouchFromButton::GetButtonInput(const int row_index, const bool is_new) {
+ binding_list_model->item(row_index, 0)->setText(tr("[press key]"));
+
+ input_setter = [this, row_index, is_new](const Common::ParamPackage& params,
+ const bool cancel) {
+ auto* cell = binding_list_model->item(row_index, 0);
+ if (cancel) {
+ if (is_new) {
+ binding_list_model->removeRow(row_index);
+ } else {
+ cell->setText(
+ ButtonToText(Common::ParamPackage{cell->data().toString().toStdString()}));
+ }
+ } else {
+ cell->setText(ButtonToText(params));
+ cell->setData(QString::fromStdString(params.Serialize()));
+ }
+ };
+
+ device_pollers = input_subsystem->GetPollers(InputCommon::Polling::DeviceType::Button);
+
+ for (auto& poller : device_pollers) {
+ poller->Start();
+ }
+
+ grabKeyboard();
+ grabMouse();
+ qApp->setOverrideCursor(QCursor(Qt::CursorShape::ArrowCursor));
+ timeout_timer->start(5000); // Cancel after 5 seconds
+ poll_timer->start(200); // Check for new inputs every 200ms
+}
+
+void ConfigureTouchFromButton::NewBinding(const QPoint& pos) {
+ auto* button = new QStandardItem();
+ button->setEditable(false);
+ auto* x_coord = new QStandardItem(QString::number(pos.x()));
+ auto* y_coord = new QStandardItem(QString::number(pos.y()));
+
+ const int dot_id = ui->bottom_screen->AddDot(pos.x(), pos.y());
+ button->setData(dot_id, DataRoleDot);
+
+ binding_list_model->appendRow({button, x_coord, y_coord});
+ ui->binding_list->setFocus();
+ ui->binding_list->setCurrentIndex(button->index());
+
+ GetButtonInput(binding_list_model->rowCount() - 1, true);
+}
+
+void ConfigureTouchFromButton::EditBinding(const QModelIndex& qi) {
+ if (qi.row() >= 0 && qi.column() == 0) {
+ GetButtonInput(qi.row(), false);
+ }
+}
+
+void ConfigureTouchFromButton::DeleteBinding() {
+ const int row_index = ui->binding_list->currentIndex().row();
+ if (row_index < 0) {
+ return;
+ }
+ ui->bottom_screen->RemoveDot(binding_list_model->index(row_index, 0).data(DataRoleDot).toInt());
+ binding_list_model->removeRow(row_index);
+}
+
+void ConfigureTouchFromButton::OnBindingSelection(const QItemSelection& selected,
+ const QItemSelection& deselected) {
+ ui->button_delete_bind->setEnabled(!selected.isEmpty());
+ if (!selected.isEmpty()) {
+ const auto dot_data = selected.indexes().first().data(DataRoleDot);
+ if (dot_data.isValid()) {
+ ui->bottom_screen->HighlightDot(dot_data.toInt());
+ }
+ }
+ if (!deselected.isEmpty()) {
+ const auto dot_data = deselected.indexes().first().data(DataRoleDot);
+ if (dot_data.isValid()) {
+ ui->bottom_screen->HighlightDot(dot_data.toInt(), false);
+ }
+ }
+}
+
+void ConfigureTouchFromButton::OnBindingChanged(QStandardItem* item) {
+ if (item->column() == 0) {
+ return;
+ }
+
+ const bool blocked = binding_list_model->blockSignals(true);
+ item->setText(QString::number(
+ std::clamp(item->text().toInt(), 0,
+ static_cast<int>((item->column() == 1 ? Layout::ScreenUndocked::Width
+ : Layout::ScreenUndocked::Height) -
+ 1))));
+ binding_list_model->blockSignals(blocked);
+
+ const auto dot_data = binding_list_model->index(item->row(), 0).data(DataRoleDot);
+ if (dot_data.isValid()) {
+ ui->bottom_screen->MoveDot(dot_data.toInt(),
+ binding_list_model->item(item->row(), 1)->text().toInt(),
+ binding_list_model->item(item->row(), 2)->text().toInt());
+ }
+}
+
+void ConfigureTouchFromButton::OnBindingDeleted(const QModelIndex& parent, int first, int last) {
+ for (int i = first; i <= last; ++i) {
+ const auto ix = binding_list_model->index(i, 0);
+ if (!ix.isValid()) {
+ return;
+ }
+ const auto dot_data = ix.data(DataRoleDot);
+ if (dot_data.isValid()) {
+ ui->bottom_screen->RemoveDot(dot_data.toInt());
+ }
+ }
+}
+
+void ConfigureTouchFromButton::SetActiveBinding(const int dot_id) {
+ for (int i = 0; i < binding_list_model->rowCount(); ++i) {
+ if (binding_list_model->index(i, 0).data(DataRoleDot) == dot_id) {
+ ui->binding_list->setCurrentIndex(binding_list_model->index(i, 0));
+ ui->binding_list->setFocus();
+ return;
+ }
+ }
+}
+
+void ConfigureTouchFromButton::SetCoordinates(const int dot_id, const QPoint& pos) {
+ for (int i = 0; i < binding_list_model->rowCount(); ++i) {
+ if (binding_list_model->item(i, 0)->data(DataRoleDot) == dot_id) {
+ binding_list_model->item(i, 1)->setText(QString::number(pos.x()));
+ binding_list_model->item(i, 2)->setText(QString::number(pos.y()));
+ return;
+ }
+ }
+}
+
+void ConfigureTouchFromButton::SetPollingResult(const Common::ParamPackage& params,
+ const bool cancel) {
+ releaseKeyboard();
+ releaseMouse();
+ qApp->restoreOverrideCursor();
+ timeout_timer->stop();
+ poll_timer->stop();
+ for (auto& poller : device_pollers) {
+ poller->Stop();
+ }
+ if (input_setter) {
+ (*input_setter)(params, cancel);
+ input_setter.reset();
+ }
+}
+
+void ConfigureTouchFromButton::keyPressEvent(QKeyEvent* event) {
+ if (!input_setter && event->key() == Qt::Key_Delete) {
+ DeleteBinding();
+ return;
+ }
+
+ if (!input_setter) {
+ return QDialog::keyPressEvent(event);
+ }
+
+ if (event->key() != Qt::Key_Escape) {
+ SetPollingResult(Common::ParamPackage{InputCommon::GenerateKeyboardParam(event->key())},
+ false);
+ } else {
+ SetPollingResult({}, true);
+ }
+}
+
+void ConfigureTouchFromButton::ApplyConfiguration() {
+ SaveCurrentMapping();
+ accept();
+}
+
+int ConfigureTouchFromButton::GetSelectedIndex() const {
+ return selected_index;
+}
+
+std::vector<Settings::TouchFromButtonMap> ConfigureTouchFromButton::GetMaps() const {
+ return touch_maps;
+}
+
+TouchScreenPreview::TouchScreenPreview(QWidget* parent) : QFrame(parent) {
+ setBackgroundRole(QPalette::ColorRole::Base);
+}
+
+TouchScreenPreview::~TouchScreenPreview() = default;
+
+void TouchScreenPreview::SetCoordLabel(QLabel* const label) {
+ coord_label = label;
+}
+
+int TouchScreenPreview::AddDot(const int device_x, const int device_y) {
+ QFont dot_font{QStringLiteral("monospace")};
+ dot_font.setStyleHint(QFont::Monospace);
+ dot_font.setPointSize(20);
+
+ auto* dot = new QLabel(this);
+ dot->setAttribute(Qt::WA_TranslucentBackground);
+ dot->setFont(dot_font);
+ dot->setText(QChar(0xD7)); // U+00D7 Multiplication Sign
+ dot->setAlignment(Qt::AlignmentFlag::AlignCenter);
+ dot->setProperty(PropId, ++max_dot_id);
+ dot->setProperty(PropX, device_x);
+ dot->setProperty(PropY, device_y);
+ dot->setCursor(Qt::CursorShape::PointingHandCursor);
+ dot->setMouseTracking(true);
+ dot->installEventFilter(this);
+ dot->show();
+ PositionDot(dot, device_x, device_y);
+ dots.emplace_back(max_dot_id, dot);
+ return max_dot_id;
+}
+
+void TouchScreenPreview::RemoveDot(const int id) {
+ const auto iter = std::find_if(dots.begin(), dots.end(),
+ [id](const auto& entry) { return entry.first == id; });
+ if (iter == dots.cend()) {
+ return;
+ }
+
+ iter->second->deleteLater();
+ dots.erase(iter);
+}
+
+void TouchScreenPreview::HighlightDot(const int id, const bool active) const {
+ for (const auto& dot : dots) {
+ if (dot.first == id) {
+ // use color property from the stylesheet, or fall back to the default palette
+ if (dot_highlight_color.isValid()) {
+ dot.second->setStyleSheet(
+ active ? QStringLiteral("color: %1").arg(dot_highlight_color.name())
+ : QString{});
+ } else {
+ dot.second->setForegroundRole(active ? QPalette::ColorRole::LinkVisited
+ : QPalette::ColorRole::NoRole);
+ }
+ if (active) {
+ dot.second->raise();
+ }
+ return;
+ }
+ }
+}
+
+void TouchScreenPreview::MoveDot(const int id, const int device_x, const int device_y) const {
+ const auto iter = std::find_if(dots.begin(), dots.end(),
+ [id](const auto& entry) { return entry.first == id; });
+ if (iter == dots.cend()) {
+ return;
+ }
+
+ iter->second->setProperty(PropX, device_x);
+ iter->second->setProperty(PropY, device_y);
+ PositionDot(iter->second, device_x, device_y);
+}
+
+void TouchScreenPreview::resizeEvent(QResizeEvent* event) {
+ if (ignore_resize) {
+ return;
+ }
+
+ const int target_width = std::min(width(), height() * 4 / 3);
+ const int target_height = std::min(height(), width() * 3 / 4);
+ if (target_width == width() && target_height == height()) {
+ return;
+ }
+ ignore_resize = true;
+ setGeometry((parentWidget()->contentsRect().width() - target_width) / 2, y(), target_width,
+ target_height);
+ ignore_resize = false;
+
+ if (event->oldSize().width() != target_width || event->oldSize().height() != target_height) {
+ for (const auto& dot : dots) {
+ PositionDot(dot.second);
+ }
+ }
+}
+
+void TouchScreenPreview::mouseMoveEvent(QMouseEvent* event) {
+ if (!coord_label) {
+ return;
+ }
+ const auto pos = MapToDeviceCoords(event->x(), event->y());
+ if (pos) {
+ coord_label->setText(QStringLiteral("X: %1, Y: %2").arg(pos->x()).arg(pos->y()));
+ } else {
+ coord_label->clear();
+ }
+}
+
+void TouchScreenPreview::leaveEvent(QEvent* event) {
+ if (coord_label) {
+ coord_label->clear();
+ }
+}
+
+void TouchScreenPreview::mousePressEvent(QMouseEvent* event) {
+ if (event->button() != Qt::MouseButton::LeftButton) {
+ return;
+ }
+ const auto pos = MapToDeviceCoords(event->x(), event->y());
+ if (pos) {
+ emit DotAdded(*pos);
+ }
+}
+
+bool TouchScreenPreview::eventFilter(QObject* obj, QEvent* event) {
+ switch (event->type()) {
+ case QEvent::Type::MouseButtonPress: {
+ const auto mouse_event = static_cast<QMouseEvent*>(event);
+ if (mouse_event->button() != Qt::MouseButton::LeftButton) {
+ break;
+ }
+ emit DotSelected(obj->property(PropId).toInt());
+
+ drag_state.dot = qobject_cast<QLabel*>(obj);
+ drag_state.start_pos = mouse_event->globalPos();
+ return true;
+ }
+ case QEvent::Type::MouseMove: {
+ if (!drag_state.dot) {
+ break;
+ }
+ const auto mouse_event = static_cast<QMouseEvent*>(event);
+ if (!drag_state.active) {
+ drag_state.active =
+ (mouse_event->globalPos() - drag_state.start_pos).manhattanLength() >=
+ QApplication::startDragDistance();
+ if (!drag_state.active) {
+ break;
+ }
+ }
+ auto current_pos = mapFromGlobal(mouse_event->globalPos());
+ current_pos.setX(std::clamp(current_pos.x(), contentsMargins().left(),
+ contentsMargins().left() + contentsRect().width() - 1));
+ current_pos.setY(std::clamp(current_pos.y(), contentsMargins().top(),
+ contentsMargins().top() + contentsRect().height() - 1));
+ const auto device_coord = MapToDeviceCoords(current_pos.x(), current_pos.y());
+ if (device_coord) {
+ drag_state.dot->setProperty(PropX, device_coord->x());
+ drag_state.dot->setProperty(PropY, device_coord->y());
+ PositionDot(drag_state.dot, device_coord->x(), device_coord->y());
+ emit DotMoved(drag_state.dot->property(PropId).toInt(), *device_coord);
+ if (coord_label) {
+ coord_label->setText(
+ QStringLiteral("X: %1, Y: %2").arg(device_coord->x()).arg(device_coord->y()));
+ }
+ }
+ return true;
+ }
+ case QEvent::Type::MouseButtonRelease: {
+ drag_state.dot.clear();
+ drag_state.active = false;
+ return true;
+ }
+ default:
+ break;
+ }
+ return obj->eventFilter(obj, event);
+}
+
+std::optional<QPoint> TouchScreenPreview::MapToDeviceCoords(const int screen_x,
+ const int screen_y) const {
+ const float t_x = 0.5f + static_cast<float>(screen_x - contentsMargins().left()) *
+ (Layout::ScreenUndocked::Width - 1) / (contentsRect().width() - 1);
+ const float t_y = 0.5f + static_cast<float>(screen_y - contentsMargins().top()) *
+ (Layout::ScreenUndocked::Height - 1) /
+ (contentsRect().height() - 1);
+ if (t_x >= 0.5f && t_x < Layout::ScreenUndocked::Width && t_y >= 0.5f &&
+ t_y < Layout::ScreenUndocked::Height) {
+
+ return QPoint{static_cast<int>(t_x), static_cast<int>(t_y)};
+ }
+ return std::nullopt;
+}
+
+void TouchScreenPreview::PositionDot(QLabel* const dot, const int device_x,
+ const int device_y) const {
+ const float device_coord_x =
+ static_cast<float>(device_x >= 0 ? device_x : dot->property(PropX).toInt());
+ int x_coord = static_cast<int>(
+ device_coord_x * (contentsRect().width() - 1) / (Layout::ScreenUndocked::Width - 1) +
+ contentsMargins().left() - static_cast<float>(dot->width()) / 2 + 0.5f);
+
+ const float device_coord_y =
+ static_cast<float>(device_y >= 0 ? device_y : dot->property(PropY).toInt());
+ const int y_coord = static_cast<int>(
+ device_coord_y * (contentsRect().height() - 1) / (Layout::ScreenUndocked::Height - 1) +
+ contentsMargins().top() - static_cast<float>(dot->height()) / 2 + 0.5f);
+
+ dot->move(x_coord, y_coord);
+}
diff --git a/src/yuzu/configuration/configure_touch_from_button.h b/src/yuzu/configuration/configure_touch_from_button.h
new file mode 100644
index 000000000..d9513e3bc
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_from_button.h
@@ -0,0 +1,92 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <functional>
+#include <memory>
+#include <optional>
+#include <vector>
+#include <QDialog>
+
+class QItemSelection;
+class QModelIndex;
+class QStandardItemModel;
+class QStandardItem;
+class QTimer;
+
+namespace Common {
+class ParamPackage;
+}
+
+namespace InputCommon {
+class InputSubsystem;
+}
+
+namespace InputCommon::Polling {
+class DevicePoller;
+}
+
+namespace Settings {
+struct TouchFromButtonMap;
+}
+
+namespace Ui {
+class ConfigureTouchFromButton;
+}
+
+class ConfigureTouchFromButton : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureTouchFromButton(QWidget* parent,
+ const std::vector<Settings::TouchFromButtonMap>& touch_maps,
+ InputCommon::InputSubsystem* input_subsystem_,
+ int default_index = 0);
+ ~ConfigureTouchFromButton() override;
+
+ int GetSelectedIndex() const;
+ std::vector<Settings::TouchFromButtonMap> GetMaps() const;
+
+public slots:
+ void ApplyConfiguration();
+ void NewBinding(const QPoint& pos);
+ void SetActiveBinding(int dot_id);
+ void SetCoordinates(int dot_id, const QPoint& pos);
+
+protected:
+ void showEvent(QShowEvent* ev) override;
+ void keyPressEvent(QKeyEvent* event) override;
+
+private slots:
+ void NewMapping();
+ void DeleteMapping();
+ void RenameMapping();
+ void EditBinding(const QModelIndex& qi);
+ void DeleteBinding();
+ void OnBindingSelection(const QItemSelection& selected, const QItemSelection& deselected);
+ void OnBindingChanged(QStandardItem* item);
+ void OnBindingDeleted(const QModelIndex& parent, int first, int last);
+
+private:
+ void SetConfiguration();
+ void UpdateUiDisplay();
+ void ConnectEvents();
+ void GetButtonInput(int row_index, bool is_new);
+ void SetPollingResult(const Common::ParamPackage& params, bool cancel);
+ void SaveCurrentMapping();
+
+ std::unique_ptr<Ui::ConfigureTouchFromButton> ui;
+ std::vector<Settings::TouchFromButtonMap> touch_maps;
+ QStandardItemModel* binding_list_model;
+ InputCommon::InputSubsystem* input_subsystem;
+ int selected_index;
+
+ std::unique_ptr<QTimer> timeout_timer;
+ std::unique_ptr<QTimer> poll_timer;
+ std::vector<std::unique_ptr<InputCommon::Polling::DevicePoller>> device_pollers;
+ std::optional<std::function<void(const Common::ParamPackage&, bool)>> input_setter;
+
+ static constexpr int DataRoleDot = Qt::ItemDataRole::UserRole + 2;
+};
diff --git a/src/yuzu/configuration/configure_touch_from_button.ui b/src/yuzu/configuration/configure_touch_from_button.ui
new file mode 100644
index 000000000..757219d54
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_from_button.ui
@@ -0,0 +1,221 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureTouchFromButton</class>
+ <widget class="QDialog" name="ConfigureTouchFromButton">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>500</width>
+ <height>500</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Touchscreen Mappings</string>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <widget class="QLabel" name="label">
+ <property name="text">
+ <string>Mapping:</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QComboBox" name="mapping">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="button_new">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>New</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="button_delete">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Delete</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="button_rename">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Maximum" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Rename</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="Line" name="line">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_2">
+ <item>
+ <widget class="QLabel" name="label_2">
+ <property name="text">
+ <string>Click the bottom area to add a point, then press a button to bind.
+Drag points to change position, or double-click table cells to edit values.</string>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="button_delete_bind">
+ <property name="text">
+ <string>Delete Point</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QTreeView" name="binding_list">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="rootIsDecorated">
+ <bool>false</bool>
+ </property>
+ <property name="uniformRowHeights">
+ <bool>true</bool>
+ </property>
+ <property name="itemsExpandable">
+ <bool>false</bool>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="TouchScreenPreview" name="bottom_screen">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Expanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>160</width>
+ <height>120</height>
+ </size>
+ </property>
+ <property name="baseSize">
+ <size>
+ <width>320</width>
+ <height>240</height>
+ </size>
+ </property>
+ <property name="cursor">
+ <cursorShape>CrossCursor</cursorShape>
+ </property>
+ <property name="mouseTracking">
+ <bool>true</bool>
+ </property>
+ <property name="autoFillBackground">
+ <bool>true</bool>
+ </property>
+ <property name="frameShape">
+ <enum>QFrame::StyledPanel</enum>
+ </property>
+ <property name="frameShadow">
+ <enum>QFrame::Sunken</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout_3">
+ <item>
+ <widget class="QLabel" name="coord_label">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Preferred">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="textFormat">
+ <enum>Qt::PlainText</enum>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBox">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <customwidgets>
+ <customwidget>
+ <class>TouchScreenPreview</class>
+ <extends>QFrame</extends>
+ <header>yuzu/configuration/configure_touch_widget.h</header>
+ <container>1</container>
+ </customwidget>
+ </customwidgets>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBox</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureTouchFromButton</receiver>
+ <slot>reject()</slot>
+ </connection>
+ </connections>
+</ui>
diff --git a/src/yuzu/configuration/configure_touch_widget.h b/src/yuzu/configuration/configure_touch_widget.h
new file mode 100644
index 000000000..347b46583
--- /dev/null
+++ b/src/yuzu/configuration/configure_touch_widget.h
@@ -0,0 +1,62 @@
+// Copyright 2020 Citra Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <optional>
+#include <utility>
+#include <vector>
+#include <QFrame>
+#include <QPointer>
+
+class QLabel;
+
+// Widget for representing touchscreen coordinates
+class TouchScreenPreview : public QFrame {
+ Q_OBJECT
+ Q_PROPERTY(QColor dotHighlightColor MEMBER dot_highlight_color)
+
+public:
+ explicit TouchScreenPreview(QWidget* parent);
+ ~TouchScreenPreview() override;
+
+ void SetCoordLabel(QLabel*);
+ int AddDot(int device_x, int device_y);
+ void RemoveDot(int id);
+ void HighlightDot(int id, bool active = true) const;
+ void MoveDot(int id, int device_x, int device_y) const;
+
+signals:
+ void DotAdded(const QPoint& pos);
+ void DotSelected(int dot_id);
+ void DotMoved(int dot_id, const QPoint& pos);
+
+protected:
+ void resizeEvent(QResizeEvent*) override;
+ void mouseMoveEvent(QMouseEvent*) override;
+ void leaveEvent(QEvent*) override;
+ void mousePressEvent(QMouseEvent*) override;
+ bool eventFilter(QObject*, QEvent*) override;
+
+private:
+ std::optional<QPoint> MapToDeviceCoords(int screen_x, int screen_y) const;
+ void PositionDot(QLabel* dot, int device_x = -1, int device_y = -1) const;
+
+ bool ignore_resize = false;
+ QPointer<QLabel> coord_label;
+
+ std::vector<std::pair<int, QLabel*>> dots;
+ int max_dot_id = 0;
+ QColor dot_highlight_color;
+ static constexpr char PropId[] = "dot_id";
+ static constexpr char PropX[] = "device_x";
+ static constexpr char PropY[] = "device_y";
+
+ struct DragState {
+ bool active = false;
+ QPointer<QLabel> dot;
+ QPoint start_pos;
+ };
+ DragState drag_state;
+};
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 @@
<signal>accepted()</signal>
<receiver>ConfigureTouchscreenAdvanced</receiver>
<slot>accept()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>140</x>
- <y>318</y>
- </hint>
- <hint type="destinationlabel">
- <x>140</x>
- <y>169</y>
- </hint>
- </hints>
</connection>
<connection>
<sender>buttonBox</sender>
<signal>rejected()</signal>
<receiver>ConfigureTouchscreenAdvanced</receiver>
<slot>reject()</slot>
- <hints>
- <hint type="sourcelabel">
- <x>140</x>
- <y>318</y>
- </hint>
- <hint type="destinationlabel">
- <x>140</x>
- <y>169</y>
- </hint>
- </hints>
- </connection>
+ </connection>
</connections>
</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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <algorithm>
+#include <unordered_map>
+
+#include <fmt/format.h>
+
+#include "common/param_package.h"
+#include "core/settings.h"
+#include "ui_configure_vibration.h"
+#include "yuzu/configuration/configure_vibration.h"
+
+ConfigureVibration::ConfigureVibration(QWidget* parent)
+ : QDialog(parent), ui(std::make_unique<Ui::ConfigureVibration>()) {
+ ui->setupUi(this);
+
+ vibration_groupboxes = {
+ ui->vibrationGroupPlayer1, ui->vibrationGroupPlayer2, ui->vibrationGroupPlayer3,
+ ui->vibrationGroupPlayer4, ui->vibrationGroupPlayer5, ui->vibrationGroupPlayer6,
+ ui->vibrationGroupPlayer7, ui->vibrationGroupPlayer8,
+ };
+
+ vibration_spinboxes = {
+ ui->vibrationSpinPlayer1, ui->vibrationSpinPlayer2, ui->vibrationSpinPlayer3,
+ ui->vibrationSpinPlayer4, ui->vibrationSpinPlayer5, ui->vibrationSpinPlayer6,
+ ui->vibrationSpinPlayer7, ui->vibrationSpinPlayer8,
+ };
+
+ const auto& players = Settings::values.players.GetValue();
+
+ for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
+ vibration_groupboxes[i]->setChecked(players[i].vibration_enabled);
+ vibration_spinboxes[i]->setValue(players[i].vibration_strength);
+ }
+
+ ui->checkBoxAccurateVibration->setChecked(
+ Settings::values.enable_accurate_vibrations.GetValue());
+
+ if (!Settings::IsConfiguringGlobal()) {
+ ui->checkBoxAccurateVibration->setDisabled(true);
+ }
+
+ RetranslateUI();
+}
+
+ConfigureVibration::~ConfigureVibration() = default;
+
+void ConfigureVibration::ApplyConfiguration() {
+ auto& players = Settings::values.players.GetValue();
+
+ for (std::size_t i = 0; i < NUM_PLAYERS; ++i) {
+ players[i].vibration_enabled = vibration_groupboxes[i]->isChecked();
+ players[i].vibration_strength = vibration_spinboxes[i]->value();
+ }
+
+ Settings::values.enable_accurate_vibrations.SetValue(
+ ui->checkBoxAccurateVibration->isChecked());
+}
+
+void ConfigureVibration::SetVibrationDevices(std::size_t player_index) {
+ using namespace Settings::NativeButton;
+ static constexpr std::array<std::array<Settings::NativeButton::Values, 6>, 2> buttons{{
+ {DLeft, DUp, DRight, DDown, L, ZL}, // Left Buttons
+ {A, B, X, Y, R, ZR}, // Right Buttons
+ }};
+
+ auto& player = Settings::values.players.GetValue()[player_index];
+
+ for (std::size_t device_idx = 0; device_idx < buttons.size(); ++device_idx) {
+ std::unordered_map<std::string, int> params_count;
+
+ for (const auto button_index : buttons[device_idx]) {
+ const auto& player_button = player.buttons[button_index];
+
+ if (params_count.find(player_button) != params_count.end()) {
+ ++params_count[player_button];
+ continue;
+ }
+
+ params_count.insert_or_assign(player_button, 1);
+ }
+
+ const auto it = std::max_element(
+ params_count.begin(), params_count.end(),
+ [](const auto& lhs, const auto& rhs) { return lhs.second < rhs.second; });
+
+ auto& vibration_param_str = player.vibrations[device_idx];
+ vibration_param_str.clear();
+
+ if (it->first.empty()) {
+ continue;
+ }
+
+ const auto param = Common::ParamPackage(it->first);
+
+ const auto engine = param.Get("engine", "");
+ const auto guid = param.Get("guid", "");
+ const auto port = param.Get("port", "");
+
+ if (engine.empty() || engine == "keyboard" || engine == "mouse") {
+ continue;
+ }
+
+ vibration_param_str += fmt::format("engine:{}", engine);
+
+ if (!port.empty()) {
+ vibration_param_str += fmt::format(",port:{}", port);
+ }
+ if (!guid.empty()) {
+ vibration_param_str += fmt::format(",guid:{}", guid);
+ }
+ }
+
+ if (player.vibrations[0] != player.vibrations[1]) {
+ return;
+ }
+
+ if (!player.vibrations[0].empty() &&
+ player.controller_type != Settings::ControllerType::RightJoycon) {
+ player.vibrations[1].clear();
+ } else if (!player.vibrations[1].empty() &&
+ player.controller_type == Settings::ControllerType::RightJoycon) {
+ player.vibrations[0].clear();
+ }
+}
+
+void ConfigureVibration::SetAllVibrationDevices() {
+ // Set vibration devices for all player indices including handheld
+ for (std::size_t player_idx = 0; player_idx < NUM_PLAYERS + 1; ++player_idx) {
+ SetVibrationDevices(player_idx);
+ }
+}
+
+void ConfigureVibration::changeEvent(QEvent* event) {
+ if (event->type() == QEvent::LanguageChange) {
+ RetranslateUI();
+ }
+
+ QDialog::changeEvent(event);
+}
+
+void ConfigureVibration::RetranslateUI() {
+ ui->retranslateUi(this);
+}
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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <array>
+#include <memory>
+#include <QDialog>
+
+class QGroupBox;
+class QSpinBox;
+
+namespace Ui {
+class ConfigureVibration;
+}
+
+class ConfigureVibration : public QDialog {
+ Q_OBJECT
+
+public:
+ explicit ConfigureVibration(QWidget* parent);
+ ~ConfigureVibration() override;
+
+ void ApplyConfiguration();
+
+ static void SetVibrationDevices(std::size_t player_index);
+ static void SetAllVibrationDevices();
+
+private:
+ void changeEvent(QEvent* event) override;
+ void RetranslateUI();
+
+ std::unique_ptr<Ui::ConfigureVibration> ui;
+
+ static constexpr std::size_t NUM_PLAYERS = 8;
+
+ // Groupboxes encapsulating the vibration strength spinbox.
+ std::array<QGroupBox*, NUM_PLAYERS> vibration_groupboxes;
+
+ // Spinboxes representing the vibration strength percentage.
+ std::array<QSpinBox*, NUM_PLAYERS> vibration_spinboxes;
+};
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 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ConfigureVibration</class>
+ <widget class="QDialog" name="ConfigureVibration">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>364</width>
+ <height>242</height>
+ </rect>
+ </property>
+ <property name="windowTitle">
+ <string>Configure Vibration</string>
+ </property>
+ <property name="styleSheet">
+ <string notr="true"/>
+ </property>
+ <layout class="QVBoxLayout">
+ <item>
+ <widget class="QGroupBox" name="vibrationStrengthGroup">
+ <property name="title">
+ <string>Vibration</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_3" stretch="0,0">
+ <property name="leftMargin">
+ <number>9</number>
+ </property>
+ <property name="topMargin">
+ <number>9</number>
+ </property>
+ <property name="rightMargin">
+ <number>9</number>
+ </property>
+ <property name="bottomMargin">
+ <number>9</number>
+ </property>
+ <item>
+ <widget class="QWidget" name="player14Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_4">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer1">
+ <property name="title">
+ <string>Player 1</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_8">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer1">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer2">
+ <property name="title">
+ <string>Player 2</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_9">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer2">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer3">
+ <property name="title">
+ <string>Player 3</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_10">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer3">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer4">
+ <property name="title">
+ <string>Player 4</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_11">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer4">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QWidget" name="player58Widget" native="true">
+ <layout class="QHBoxLayout" name="horizontalLayout_6">
+ <property name="leftMargin">
+ <number>0</number>
+ </property>
+ <property name="topMargin">
+ <number>0</number>
+ </property>
+ <property name="rightMargin">
+ <number>0</number>
+ </property>
+ <property name="bottomMargin">
+ <number>0</number>
+ </property>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer7">
+ <property name="title">
+ <string>Player 5</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_14">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer7">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer8">
+ <property name="title">
+ <string>Player 6</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_15">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer8">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer5">
+ <property name="title">
+ <string>Player 7</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_12">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer5">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationGroupPlayer6">
+ <property name="title">
+ <string>Player 8</string>
+ </property>
+ <property name="checkable">
+ <bool>true</bool>
+ </property>
+ <layout class="QHBoxLayout" name="horizontalLayout_13">
+ <property name="leftMargin">
+ <number>3</number>
+ </property>
+ <property name="topMargin">
+ <number>3</number>
+ </property>
+ <property name="rightMargin">
+ <number>3</number>
+ </property>
+ <property name="bottomMargin">
+ <number>3</number>
+ </property>
+ <item>
+ <widget class="QSpinBox" name="vibrationSpinPlayer6">
+ <property name="minimumSize">
+ <size>
+ <width>68</width>
+ <height>21</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>68</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ <property name="suffix">
+ <string>%</string>
+ </property>
+ <property name="minimum">
+ <number>1</number>
+ </property>
+ <property name="maximum">
+ <number>150</number>
+ </property>
+ <property name="value">
+ <number>100</number>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <widget class="QGroupBox" name="vibrationSettingsGroup">
+ <property name="title">
+ <string>Settings</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <widget class="QCheckBox" name="checkBoxAccurateVibration">
+ <property name="text">
+ <string>Enable Accurate Vibration</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <spacer name="spacerVibration">
+ <property name="orientation">
+ <enum>Qt::Vertical</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>167</width>
+ <height>55</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QDialogButtonBox" name="buttonBoxVibration">
+ <property name="standardButtons">
+ <set>QDialogButtonBox::Cancel|QDialogButtonBox::Ok</set>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections>
+ <connection>
+ <sender>buttonBoxVibration</sender>
+ <signal>accepted()</signal>
+ <receiver>ConfigureVibration</receiver>
+ <slot>accept()</slot>
+ </connection>
+ <connection>
+ <sender>buttonBoxVibration</sender>
+ <signal>rejected()</signal>
+ <receiver>ConfigureVibration</receiver>
+ <slot>reject()</slot>
+ </connection>
+ </connections>
+</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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#include <fmt/format.h>
+
+#include "common/common_paths.h"
+#include "common/file_util.h"
+#include "yuzu/configuration/config.h"
+#include "yuzu/configuration/input_profiles.h"
+
+namespace FS = Common::FS;
+
+namespace {
+
+bool ProfileExistsInFilesystem(std::string_view profile_name) {
+ return FS::Exists(fmt::format("{}input" DIR_SEP "{}.ini",
+ FS::GetUserPath(FS::UserPath::ConfigDir), profile_name));
+}
+
+bool IsINI(std::string_view filename) {
+ const std::size_t index = filename.rfind('.');
+
+ if (index == std::string::npos) {
+ return false;
+ }
+
+ return filename.substr(index) == ".ini";
+}
+
+std::string GetNameWithoutExtension(const std::string& filename) {
+ const std::size_t index = filename.rfind('.');
+
+ if (index == std::string::npos) {
+ return filename;
+ }
+
+ return filename.substr(0, index);
+}
+
+} // namespace
+
+InputProfiles::InputProfiles() {
+ const std::string input_profile_loc =
+ fmt::format("{}input", FS::GetUserPath(FS::UserPath::ConfigDir));
+
+ FS::ForeachDirectoryEntry(
+ nullptr, input_profile_loc,
+ [this](u64* entries_out, const std::string& directory, const std::string& filename) {
+ if (IsINI(filename) && IsProfileNameValid(GetNameWithoutExtension(filename))) {
+ map_profiles.insert_or_assign(
+ GetNameWithoutExtension(filename),
+ std::make_unique<Config>(GetNameWithoutExtension(filename),
+ Config::ConfigType::InputProfile));
+ }
+ return true;
+ });
+}
+
+InputProfiles::~InputProfiles() = default;
+
+std::vector<std::string> InputProfiles::GetInputProfileNames() {
+ std::vector<std::string> profile_names;
+ profile_names.reserve(map_profiles.size());
+
+ for (const auto& [profile_name, config] : map_profiles) {
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ DeleteProfile(profile_name);
+ continue;
+ }
+
+ profile_names.push_back(profile_name);
+ }
+
+ return profile_names;
+}
+
+bool InputProfiles::IsProfileNameValid(std::string_view profile_name) {
+ return profile_name.find_first_of("<>:;\"/\\|,.!?*") == std::string::npos;
+}
+
+bool InputProfiles::CreateProfile(const std::string& profile_name, std::size_t player_index) {
+ if (ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ map_profiles.insert_or_assign(
+ profile_name, std::make_unique<Config>(profile_name, Config::ConfigType::InputProfile));
+
+ return SaveProfile(profile_name, player_index);
+}
+
+bool InputProfiles::DeleteProfile(const std::string& profile_name) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name) ||
+ FS::Delete(map_profiles[profile_name]->GetConfigFilePath())) {
+ map_profiles.erase(profile_name);
+ }
+
+ return !ProfileExistsInMap(profile_name) && !ProfileExistsInFilesystem(profile_name);
+}
+
+bool InputProfiles::LoadProfile(const std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ if (!ProfileExistsInFilesystem(profile_name)) {
+ map_profiles.erase(profile_name);
+ return false;
+ }
+
+ map_profiles[profile_name]->ReadControlPlayerValue(player_index);
+ return true;
+}
+
+bool InputProfiles::SaveProfile(const std::string& profile_name, std::size_t player_index) {
+ if (!ProfileExistsInMap(profile_name)) {
+ return false;
+ }
+
+ map_profiles[profile_name]->SaveControlPlayerValue(player_index);
+ return true;
+}
+
+bool InputProfiles::ProfileExistsInMap(const std::string& profile_name) const {
+ return map_profiles.find(profile_name) != map_profiles.end();
+}
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 @@
+// Copyright 2020 yuzu Emulator Project
+// Licensed under GPLv2 or any later version
+// Refer to the license.txt file included.
+
+#pragma once
+
+#include <string>
+#include <string_view>
+#include <unordered_map>
+
+class Config;
+
+class InputProfiles {
+
+public:
+ explicit InputProfiles();
+ virtual ~InputProfiles();
+
+ std::vector<std::string> GetInputProfileNames();
+
+ static bool IsProfileNameValid(std::string_view profile_name);
+
+ bool CreateProfile(const std::string& profile_name, std::size_t player_index);
+ bool DeleteProfile(const std::string& profile_name);
+ bool LoadProfile(const std::string& profile_name, std::size_t player_index);
+ bool SaveProfile(const std::string& profile_name, std::size_t player_index);
+
+private:
+ bool ProfileExistsInMap(const std::string& profile_name) const;
+
+ std::unordered_map<std::string, std::unique_ptr<Config>> map_profiles;
+};
diff --git a/src/yuzu/game_list.cpp b/src/yuzu/game_list.cpp
index 6a71d9644..70d865112 100644
--- a/src/yuzu/game_list.cpp
+++ b/src/yuzu/game_list.cpp
@@ -25,7 +25,8 @@
#include "yuzu/main.h"
#include "yuzu/uisettings.h"
-GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist) : gamelist{gamelist} {}
+GameListSearchField::KeyReleaseEater::KeyReleaseEater(GameList* gamelist, QObject* parent)
+ : QObject(parent), gamelist{gamelist} {}
// EventFilter in order to process systemkeys while editing the searchfield
bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* event) {
@@ -56,7 +57,7 @@ bool GameListSearchField::KeyReleaseEater::eventFilter(QObject* obj, QEvent* eve
case Qt::Key_Return:
case Qt::Key_Enter: {
if (gamelist->search_field->visible == 1) {
- QString file_path = gamelist->getLastFilterResultItem();
+ const QString file_path = gamelist->GetLastFilterResultItem();
// To avoid loading error dialog loops while confirming them using enter
// Also users usually want to run a different game after closing one
@@ -83,22 +84,25 @@ void GameListSearchField::setFilterResult(int visible, int total) {
label_filter_result->setText(tr("%1 of %n result(s)", "", total).arg(visible));
}
-QString GameList::getLastFilterResultItem() const {
- QStandardItem* folder;
- QStandardItem* child;
+QString GameList::GetLastFilterResultItem() const {
QString file_path;
const int folder_count = item_model->rowCount();
+
for (int i = 0; i < folder_count; ++i) {
- folder = item_model->item(i, 0);
+ const QStandardItem* folder = item_model->item(i, 0);
const QModelIndex folder_index = folder->index();
const int children_count = folder->rowCount();
+
for (int j = 0; j < children_count; ++j) {
- if (!tree_view->isRowHidden(j, folder_index)) {
- child = folder->child(j, 0);
- file_path = child->data(GameListItemPath::FullPathRole).toString();
+ if (tree_view->isRowHidden(j, folder_index)) {
+ continue;
}
+
+ const QStandardItem* child = folder->child(j, 0);
+ file_path = child->data(GameListItemPath::FullPathRole).toString();
}
}
+
return file_path;
}
@@ -113,7 +117,7 @@ void GameListSearchField::setFocus() {
}
GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
- auto* const key_release_eater = new KeyReleaseEater(parent);
+ auto* const key_release_eater = new KeyReleaseEater(parent, this);
layout_filter = new QHBoxLayout;
layout_filter->setMargin(8);
label_filter = new QLabel;
@@ -123,7 +127,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
edit_filter->setPlaceholderText(tr("Enter pattern to filter"));
edit_filter->installEventFilter(key_release_eater);
edit_filter->setClearButtonEnabled(true);
- connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::onTextChanged);
+ connect(edit_filter, &QLineEdit::textChanged, parent, &GameList::OnTextChanged);
label_filter_result = new QLabel;
button_filter_close = new QToolButton(this);
button_filter_close->setText(QStringLiteral("X"));
@@ -133,7 +137,7 @@ GameListSearchField::GameListSearchField(GameList* parent) : QWidget{parent} {
"#000000; font-weight: bold; background: #F0F0F0; }"
"QToolButton:hover{ border: none; padding: 0px; color: "
"#EEEEEE; font-weight: bold; background: #E81123}"));
- connect(button_filter_close, &QToolButton::clicked, parent, &GameList::onFilterCloseClicked);
+ connect(button_filter_close, &QToolButton::clicked, parent, &GameList::OnFilterCloseClicked);
layout_filter->setSpacing(10);
layout_filter->addWidget(label_filter);
layout_filter->addWidget(edit_filter);
@@ -159,16 +163,22 @@ static bool ContainsAllWords(const QString& haystack, const QString& userinput)
}
// Syncs the expanded state of Game Directories with settings to persist across sessions
-void GameList::onItemExpanded(const QModelIndex& item) {
+void GameList::OnItemExpanded(const QModelIndex& item) {
const auto type = item.data(GameListItem::TypeRole).value<GameListItemType>();
- if (type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
- type == GameListItemType::UserNandDir || type == GameListItemType::SysNandDir)
- item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>()->expanded =
- tree_view->isExpanded(item);
+ const bool is_dir = type == GameListItemType::CustomDir || type == GameListItemType::SdmcDir ||
+ type == GameListItemType::UserNandDir ||
+ type == GameListItemType::SysNandDir;
+
+ if (!is_dir) {
+ return;
+ }
+
+ auto* game_dir = item.data(GameListDir::GameDirRole).value<UISettings::GameDir*>();
+ game_dir->expanded = tree_view->isExpanded(item);
}
// Event in order to filter the gamelist after editing the searchfield
-void GameList::onTextChanged(const QString& new_text) {
+void GameList::OnTextChanged(const QString& new_text) {
const int folder_count = tree_view->model()->rowCount();
QString edit_filter_text = new_text.toLower();
QStandardItem* folder;
@@ -224,7 +234,7 @@ void GameList::onTextChanged(const QString& new_text) {
}
}
-void GameList::onUpdateThemedIcons() {
+void GameList::OnUpdateThemedIcons() {
for (int i = 0; i < item_model->invisibleRootItem()->rowCount(); i++) {
QStandardItem* child = item_model->invisibleRootItem()->child(i);
@@ -276,7 +286,7 @@ void GameList::onUpdateThemedIcons() {
}
}
-void GameList::onFilterCloseClicked() {
+void GameList::OnFilterCloseClicked() {
main_window->filterBarSetChecked(false);
}
@@ -317,11 +327,11 @@ GameList::GameList(FileSys::VirtualFilesystem vfs, FileSys::ManualContentProvide
}
item_model->setSortRole(GameListItemPath::SortRole);
- connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::onUpdateThemedIcons);
+ connect(main_window, &GMainWindow::UpdateThemedIcons, this, &GameList::OnUpdateThemedIcons);
connect(tree_view, &QTreeView::activated, this, &GameList::ValidateEntry);
connect(tree_view, &QTreeView::customContextMenuRequested, this, &GameList::PopupContextMenu);
- connect(tree_view, &QTreeView::expanded, this, &GameList::onItemExpanded);
- connect(tree_view, &QTreeView::collapsed, this, &GameList::onItemExpanded);
+ connect(tree_view, &QTreeView::expanded, this, &GameList::OnItemExpanded);
+ connect(tree_view, &QTreeView::collapsed, this, &GameList::OnItemExpanded);
// We must register all custom types with the Qt Automoc system so that we are able to use
// it with signals/slots. In this case, QList falls under the umbrells of custom types.
@@ -338,17 +348,17 @@ GameList::~GameList() {
emit ShouldCancelWorker();
}
-void GameList::setFilterFocus() {
+void GameList::SetFilterFocus() {
if (tree_view->model()->rowCount() > 0) {
search_field->setFocus();
}
}
-void GameList::setFilterVisible(bool visibility) {
+void GameList::SetFilterVisible(bool visibility) {
search_field->setVisible(visibility);
}
-void GameList::clearFilter() {
+void GameList::ClearFilter() {
search_field->clear();
}
@@ -397,10 +407,11 @@ void GameList::ValidateEntry(const QModelIndex& item) {
}
}
-bool GameList::isEmpty() const {
+bool GameList::IsEmpty() const {
for (int i = 0; i < item_model->rowCount(); i++) {
const QStandardItem* child = item_model->invisibleRootItem()->child(i);
const auto type = static_cast<GameListItemType>(child->type());
+
if (!child->hasChildren() &&
(type == GameListItemType::SdmcDir || type == GameListItemType::UserNandDir ||
type == GameListItemType::SysNandDir)) {
@@ -408,11 +419,12 @@ bool GameList::isEmpty() const {
i--;
}
}
+
return !item_model->invisibleRootItem()->hasChildren();
}
-void GameList::DonePopulating(QStringList watch_list) {
- emit ShowList(!isEmpty());
+void GameList::DonePopulating(const QStringList& watch_list) {
+ emit ShowList(!IsEmpty());
item_model->invisibleRootItem()->appendRow(new GameListAddDir());
@@ -472,7 +484,7 @@ void GameList::PopupContextMenu(const QPoint& menu_location) {
context_menu.exec(tree_view->viewport()->mapToGlobal(menu_location));
}
-void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, std::string path) {
+void GameList::AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path) {
QAction* open_save_location = context_menu.addAction(tr("Open Save Data Location"));
QAction* open_mod_location = context_menu.addAction(tr("Open Mod Data Location"));
QAction* open_transferable_shader_cache =
@@ -690,12 +702,15 @@ void GameList::SaveInterfaceLayout() {
}
void GameList::LoadInterfaceLayout() {
- auto header = tree_view->header();
- if (!header->restoreState(UISettings::values.gamelist_header_state)) {
- // We are using the name column to display icons and titles
- // so make it as large as possible as default.
- header->resizeSection(COLUMN_NAME, header->width());
+ auto* header = tree_view->header();
+
+ if (header->restoreState(UISettings::values.gamelist_header_state)) {
+ return;
}
+
+ // We are using the name column to display icons and titles
+ // so make it as large as possible as default.
+ header->resizeSection(COLUMN_NAME, header->width());
}
const QStringList GameList::supported_file_extensions = {
diff --git a/src/yuzu/game_list.h b/src/yuzu/game_list.h
index 78e2ba169..58059a3c4 100644
--- a/src/yuzu/game_list.h
+++ b/src/yuzu/game_list.h
@@ -67,11 +67,11 @@ public:
FileSys::ManualContentProvider* provider, GMainWindow* parent = nullptr);
~GameList() override;
- QString getLastFilterResultItem() const;
- void clearFilter();
- void setFilterFocus();
- void setFilterVisible(bool visibility);
- bool isEmpty() const;
+ QString GetLastFilterResultItem() const;
+ void ClearFilter();
+ void SetFilterFocus();
+ void SetFilterVisible(bool visibility);
+ bool IsEmpty() const;
void LoadCompatibilityList();
void PopulateAsync(QVector<UISettings::GameDir>& game_dirs);
@@ -82,7 +82,7 @@ public:
static const QStringList supported_file_extensions;
signals:
- void GameChosen(QString game_path);
+ void GameChosen(const QString& game_path);
void ShouldCancelWorker();
void OpenFolderRequested(u64 program_id, GameListOpenTarget target,
const std::string& game_path);
@@ -99,21 +99,21 @@ signals:
void ShowList(bool show);
private slots:
- void onItemExpanded(const QModelIndex& item);
- void onTextChanged(const QString& new_text);
- void onFilterCloseClicked();
- void onUpdateThemedIcons();
+ void OnItemExpanded(const QModelIndex& item);
+ void OnTextChanged(const QString& new_text);
+ void OnFilterCloseClicked();
+ void OnUpdateThemedIcons();
private:
void AddDirEntry(GameListDir* entry_items);
void AddEntry(const QList<QStandardItem*>& entry_items, GameListDir* parent);
void ValidateEntry(const QModelIndex& item);
- void DonePopulating(QStringList watch_list);
+ void DonePopulating(const QStringList& watch_list);
void RefreshGameDirectory();
void PopupContextMenu(const QPoint& menu_location);
- void AddGamePopup(QMenu& context_menu, u64 program_id, std::string path);
+ void AddGamePopup(QMenu& context_menu, u64 program_id, const std::string& path);
void AddCustomDirPopup(QMenu& context_menu, QModelIndex selected);
void AddPermDirPopup(QMenu& context_menu, QModelIndex selected);
diff --git a/src/yuzu/game_list_p.h b/src/yuzu/game_list_p.h
index 0cd0054c8..248855aff 100644
--- a/src/yuzu/game_list_p.h
+++ b/src/yuzu/game_list_p.h
@@ -49,10 +49,10 @@ class GameListItem : public QStandardItem {
public:
// used to access type from item index
- static const int TypeRole = Qt::UserRole + 1;
- static const int SortRole = Qt::UserRole + 2;
+ static constexpr int TypeRole = Qt::UserRole + 1;
+ static constexpr int SortRole = Qt::UserRole + 2;
GameListItem() = default;
- GameListItem(const QString& string) : QStandardItem(string) {
+ explicit GameListItem(const QString& string) : QStandardItem(string) {
setData(string, SortRole);
}
};
@@ -65,10 +65,10 @@ public:
*/
class GameListItemPath : public GameListItem {
public:
- static const int TitleRole = SortRole + 1;
- static const int FullPathRole = SortRole + 2;
- static const int ProgramIdRole = SortRole + 3;
- static const int FileTypeRole = SortRole + 4;
+ static constexpr int TitleRole = SortRole + 1;
+ static constexpr int FullPathRole = SortRole + 2;
+ static constexpr int ProgramIdRole = SortRole + 3;
+ static constexpr int FileTypeRole = SortRole + 4;
GameListItemPath() = default;
GameListItemPath(const QString& game_path, const std::vector<u8>& picture_data,
@@ -110,18 +110,22 @@ public:
const auto& row1 = row_data.at(UISettings::values.row_1_text_id);
const int row2_id = UISettings::values.row_2_text_id;
- if (role == SortRole)
+ if (role == SortRole) {
return row1.toLower();
+ }
- if (row2_id == 4) // None
+ // None
+ if (row2_id == 4) {
return row1;
+ }
const auto& row2 = row_data.at(row2_id);
- if (row1 == row2)
+ if (row1 == row2) {
return row1;
+ }
- return QString(row1 + QStringLiteral("\n ") + row2);
+ return QStringLiteral("%1\n %2").arg(row1, row2);
}
return GameListItem::data(role);
@@ -131,7 +135,7 @@ public:
class GameListItemCompat : public GameListItem {
Q_DECLARE_TR_FUNCTIONS(GameListItemCompat)
public:
- static const int CompatNumberRole = SortRole;
+ static constexpr int CompatNumberRole = SortRole;
GameListItemCompat() = default;
explicit GameListItemCompat(const QString& compatibility) {
setData(type(), TypeRole);
@@ -181,7 +185,7 @@ public:
*/
class GameListItemSize : public GameListItem {
public:
- static const int SizeRole = SortRole;
+ static constexpr int SizeRole = SortRole;
GameListItemSize() = default;
explicit GameListItemSize(const qulonglong size_bytes) {
@@ -217,7 +221,7 @@ public:
class GameListDir : public GameListItem {
public:
- static const int GameDirRole = Qt::UserRole + 2;
+ static constexpr int GameDirRole = Qt::UserRole + 2;
explicit GameListDir(UISettings::GameDir& directory,
GameListItemType dir_type = GameListItemType::CustomDir)
@@ -326,7 +330,7 @@ public:
private:
class KeyReleaseEater : public QObject {
public:
- explicit KeyReleaseEater(GameList* gamelist);
+ explicit KeyReleaseEater(GameList* gamelist, QObject* parent = nullptr);
private:
GameList* gamelist = nullptr;
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;
void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
using namespace FileSys;
- const auto& cache =
- dynamic_cast<ContentProviderUnion&>(Core::System::GetInstance().GetContentProvider());
+ auto& system = Core::System::GetInstance();
+ const auto& cache = dynamic_cast<ContentProviderUnion&>(system.GetContentProvider());
- std::vector<std::pair<ContentProviderUnionSlot, ContentProviderEntry>> installed_games;
- installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application,
- ContentRecordType::Program);
+ auto installed_games = cache.ListEntriesFilterOrigin(std::nullopt, TitleType::Application,
+ ContentRecordType::Program);
if (parent_dir->type() == static_cast<int>(GameListItemType::SdmcDir)) {
installed_games = cache.ListEntriesFilterOrigin(
@@ -254,23 +253,27 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
}
for (const auto& [slot, game] : installed_games) {
- if (slot == ContentProviderUnionSlot::FrontendManual)
+ if (slot == ContentProviderUnionSlot::FrontendManual) {
continue;
+ }
const auto file = cache.GetEntryUnparsed(game.title_id, game.type);
- std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(file);
- if (!loader)
+ std::unique_ptr<Loader::AppLoader> loader = Loader::GetLoader(system, file);
+ if (!loader) {
continue;
+ }
std::vector<u8> icon;
std::string name;
u64 program_id = 0;
loader->ReadProgramId(program_id);
- const PatchManager patch{program_id};
+ const PatchManager patch{program_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto control = cache.GetEntry(game.title_id, ContentRecordType::Control);
- if (control != nullptr)
+ if (control != nullptr) {
GetMetadataFromControlNCA(patch, *control, icon, name);
+ }
emit EntryReady(MakeGameListEntry(file->GetFullPath(), name, icon, *loader, program_id,
compatibility_list, patch),
@@ -280,9 +283,11 @@ void GameListWorker::AddTitlesToGameList(GameListDir* parent_dir) {
void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_path,
unsigned int recursion, GameListDir* parent_dir) {
- const auto callback = [this, target, recursion,
- parent_dir](u64* num_entries_out, const std::string& directory,
- const std::string& virtual_name) -> bool {
+ auto& system = Core::System::GetInstance();
+
+ const auto callback = [this, target, recursion, parent_dir,
+ &system](u64* num_entries_out, const std::string& directory,
+ const std::string& virtual_name) -> bool {
if (stop_processing) {
// Breaks the callback loop.
return false;
@@ -293,7 +298,7 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
if (!is_dir &&
(HasSupportedFileExtension(physical_name) || IsExtractedNCAMain(physical_name))) {
const auto file = vfs->OpenFile(physical_name, FileSys::Mode::Read);
- auto loader = Loader::GetLoader(file);
+ auto loader = Loader::GetLoader(system, file);
if (!loader) {
return true;
}
@@ -331,7 +336,8 @@ void GameListWorker::ScanFileSystem(ScanTarget target, const std::string& dir_pa
std::string name = " ";
[[maybe_unused]] const auto res3 = loader->ReadTitle(name);
- const FileSys::PatchManager patch{program_id};
+ const FileSys::PatchManager patch{program_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
emit EntryReady(MakeGameListEntry(physical_name, name, icon, *loader, program_id,
compatibility_list, patch),
diff --git a/src/yuzu/install_dialog.h b/src/yuzu/install_dialog.h
index e4aba1b06..68e03fe4e 100644
--- a/src/yuzu/install_dialog.h
+++ b/src/yuzu/install_dialog.h
@@ -20,9 +20,8 @@ public:
explicit InstallDialog(QWidget* parent, const QStringList& files);
~InstallDialog() override;
- QStringList GetFiles() const;
- bool ShouldOverwriteFiles() const;
- int GetMinimumWidth() const;
+ [[nodiscard]] QStringList GetFiles() const;
+ [[nodiscard]] int GetMinimumWidth() const;
private:
QListWidget* file_list;
diff --git a/src/yuzu/main.cpp b/src/yuzu/main.cpp
index cd7e78eb4..e704cc656 100644
--- a/src/yuzu/main.cpp
+++ b/src/yuzu/main.cpp
@@ -11,15 +11,19 @@
#endif
// VFS includes must be before glad as they will conflict with Windows file api, which uses defines.
+#include "applets/controller.h"
#include "applets/error.h"
#include "applets/profile_select.h"
#include "applets/software_keyboard.h"
#include "applets/web_browser.h"
#include "configuration/configure_input.h"
#include "configuration/configure_per_game.h"
+#include "configuration/configure_vibration.h"
#include "core/file_sys/vfs.h"
#include "core/file_sys/vfs_real.h"
+#include "core/frontend/applets/controller.h"
#include "core/frontend/applets/general_frontend.h"
+#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/service/acc/profile_manager.h"
#include "core/hle/service/am/applet_ae.h"
#include "core/hle/service/am/applet_oe.h"
@@ -47,12 +51,14 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include <QDesktopServices>
#include <QDesktopWidget>
#include <QDialogButtonBox>
+#include <QDir>
#include <QFile>
#include <QFileDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QProgressBar>
#include <QProgressDialog>
+#include <QPushButton>
#include <QShortcut>
#include <QStatusBar>
#include <QSysInfo>
@@ -84,7 +90,6 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/file_sys/romfs.h"
#include "core/file_sys/savedata_factory.h"
#include "core/file_sys/submission_package.h"
-#include "core/frontend/applets/software_keyboard.h"
#include "core/hle/kernel/process.h"
#include "core/hle/service/am/am.h"
#include "core/hle/service/filesystem/filesystem.h"
@@ -94,6 +99,7 @@ static FileSys::VirtualFile VfsDirectoryCreateFileWrapper(const FileSys::Virtual
#include "core/perf_stats.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
+#include "input_common/main.h"
#include "video_core/gpu.h"
#include "video_core/shader_notify.h"
#include "yuzu/about_dialog.h"
@@ -186,9 +192,9 @@ static void InitializeLogging() {
}
GMainWindow::GMainWindow()
- : config(new Config()), emu_thread(nullptr),
- vfs(std::make_shared<FileSys::RealVfsFilesystem>()),
- provider(std::make_unique<FileSys::ManualContentProvider>()) {
+ : input_subsystem{std::make_shared<InputCommon::InputSubsystem>()},
+ config{std::make_unique<Config>()}, vfs{std::make_shared<FileSys::RealVfsFilesystem>()},
+ provider{std::make_unique<FileSys::ManualContentProvider>()} {
InitializeLogging();
LoadTranslation();
@@ -274,6 +280,8 @@ GMainWindow::GMainWindow()
if (args.length() >= 2) {
BootGame(args[1]);
}
+
+ MigrateConfigFiles();
}
GMainWindow::~GMainWindow() {
@@ -282,22 +290,37 @@ GMainWindow::~GMainWindow() {
delete render_window;
}
+void GMainWindow::ControllerSelectorReconfigureControllers(
+ const Core::Frontend::ControllerParameters& parameters) {
+ QtControllerSelectorDialog dialog(this, parameters, input_subsystem.get());
+
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
+ Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
+ dialog.setWindowModality(Qt::WindowModal);
+ dialog.exec();
+
+ emit ControllerSelectorReconfigureFinished();
+
+ // Don't forget to apply settings.
+ Settings::Apply();
+ config->Save();
+
+ UpdateStatusButtons();
+}
+
void GMainWindow::ProfileSelectorSelectProfile() {
- const Service::Account::ProfileManager manager;
- int index = 0;
- if (manager.GetUserCount() != 1) {
- QtProfileSelectionDialog dialog(this);
- dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
- Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
- dialog.setWindowModality(Qt::WindowModal);
- if (dialog.exec() == QDialog::Rejected) {
- emit ProfileSelectorFinishedSelection(std::nullopt);
- return;
- }
- index = dialog.GetIndex();
+ QtProfileSelectionDialog dialog(this);
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
+ Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
+ Qt::WindowCloseButtonHint);
+ dialog.setWindowModality(Qt::WindowModal);
+ if (dialog.exec() == QDialog::Rejected) {
+ emit ProfileSelectorFinishedSelection(std::nullopt);
+ return;
}
- const auto uuid = manager.GetUser(static_cast<std::size_t>(index));
+ const Service::Account::ProfileManager manager;
+ const auto uuid = manager.GetUser(static_cast<std::size_t>(dialog.GetIndex()));
if (!uuid.has_value()) {
emit ProfileSelectorFinishedSelection(std::nullopt);
return;
@@ -309,8 +332,9 @@ void GMainWindow::ProfileSelectorSelectProfile() {
void GMainWindow::SoftwareKeyboardGetText(
const Core::Frontend::SoftwareKeyboardParameters& parameters) {
QtSoftwareKeyboardDialog dialog(this, parameters);
- dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowTitleHint |
- Qt::WindowSystemMenuHint | Qt::WindowCloseButtonHint);
+ dialog.setWindowFlags(Qt::Dialog | Qt::CustomizeWindowHint | Qt::WindowStaysOnTopHint |
+ Qt::WindowTitleHint | Qt::WindowSystemMenuHint |
+ Qt::WindowCloseButtonHint);
dialog.setWindowModality(Qt::WindowModal);
if (dialog.exec() == QDialog::Rejected) {
@@ -473,7 +497,7 @@ void GMainWindow::InitializeWidgets() {
#ifdef YUZU_ENABLE_COMPATIBILITY_REPORTING
ui.action_Report_Compatibility->setVisible(true);
#endif
- render_window = new GRenderWindow(this, emu_thread.get());
+ render_window = new GRenderWindow(this, emu_thread.get(), input_subsystem);
render_window->hide();
game_list = new GameList(vfs, provider.get(), this);
@@ -529,13 +553,14 @@ void GMainWindow::InitializeWidgets() {
dock_status_button->setObjectName(QStringLiteral("TogglableStatusBarButton"));
dock_status_button->setFocusPolicy(Qt::NoFocus);
connect(dock_status_button, &QPushButton::clicked, [&] {
- Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
- dock_status_button->setChecked(Settings::values.use_docked_mode);
- OnDockedModeChanged(!Settings::values.use_docked_mode, Settings::values.use_docked_mode);
+ Settings::values.use_docked_mode.SetValue(!Settings::values.use_docked_mode.GetValue());
+ dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
+ OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
+ Settings::values.use_docked_mode.GetValue());
});
dock_status_button->setText(tr("DOCK"));
dock_status_button->setCheckable(true);
- dock_status_button->setChecked(Settings::values.use_docked_mode);
+ dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
statusBar()->insertPermanentWidget(0, dock_status_button);
// Setup ASync button
@@ -774,10 +799,11 @@ void GMainWindow::InitializeHotkeys() {
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Change Docked Mode"), this),
&QShortcut::activated, this, [&] {
- Settings::values.use_docked_mode = !Settings::values.use_docked_mode;
- OnDockedModeChanged(!Settings::values.use_docked_mode,
- Settings::values.use_docked_mode);
- dock_status_button->setChecked(Settings::values.use_docked_mode);
+ Settings::values.use_docked_mode.SetValue(
+ !Settings::values.use_docked_mode.GetValue());
+ OnDockedModeChanged(!Settings::values.use_docked_mode.GetValue(),
+ Settings::values.use_docked_mode.GetValue());
+ dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
});
connect(hotkey_registry.GetHotkey(main_window, QStringLiteral("Mute Audio"), this),
&QShortcut::activated, this,
@@ -816,7 +842,7 @@ void GMainWindow::RestoreUIState() {
OnDisplayTitleBars(ui.action_Display_Dock_Widget_Headers->isChecked());
ui.action_Show_Filter_Bar->setChecked(UISettings::values.show_filter_bar);
- game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
+ game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
ui.action_Show_Status_Bar->setChecked(UISettings::values.show_status_bar);
statusBar()->setVisible(ui.action_Show_Status_Bar->isChecked());
@@ -965,13 +991,14 @@ bool GMainWindow::LoadROM(const QString& filename) {
system.SetFilesystem(vfs);
system.SetAppletFrontendSet({
- nullptr, // Parental Controls
- std::make_unique<QtErrorDisplay>(*this), //
- nullptr, // Photo Viewer
- std::make_unique<QtProfileSelector>(*this), //
- std::make_unique<QtSoftwareKeyboard>(*this), //
- std::make_unique<QtWebBrowser>(*this), //
- nullptr, // E-Commerce
+ std::make_unique<QtControllerSelector>(*this), // Controller Selector
+ nullptr, // E-Commerce
+ std::make_unique<QtErrorDisplay>(*this), // Error Display
+ nullptr, // Parental Controls
+ nullptr, // Photo Viewer
+ std::make_unique<QtProfileSelector>(*this), // Profile Selector
+ std::make_unique<QtSoftwareKeyboard>(*this), // Software Keyboard
+ std::make_unique<QtWebBrowser>(*this), // Web Browser
});
system.RegisterHostThread();
@@ -1063,14 +1090,16 @@ void GMainWindow::BootGame(const QString& filename) {
StoreRecentFile(filename); // Put the filename on top of the list
u64 title_id{0};
-
+ auto& system = Core::System::GetInstance();
const auto v_file = Core::GetGameFileFromPath(vfs, filename.toUtf8().constData());
- const auto loader = Loader::GetLoader(v_file);
+ const auto loader = Loader::GetLoader(system, v_file);
if (!(loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success)) {
// Load per game settings
- Config per_game_config(fmt::format("{:016X}.ini", title_id), false);
+ Config per_game_config(fmt::format("{:016X}", title_id), Config::ConfigType::PerGameConfig);
}
+ ConfigureVibration::SetAllVibrationDevices();
+
Settings::LogSettings();
if (UISettings::values.select_user_on_boot) {
@@ -1115,9 +1144,13 @@ void GMainWindow::BootGame(const QString& filename) {
std::string title_name;
std::string title_version;
- const auto res = Core::System::GetInstance().GetGameName(title_name);
+ const auto res = system.GetGameName(title_name);
- const auto metadata = FileSys::PatchManager(title_id).GetControlMetadata();
+ const auto metadata = [&system, title_id] {
+ const FileSys::PatchManager pm(title_id, system.GetFileSystemController(),
+ system.GetContentProvider());
+ return pm.GetControlMetadata();
+ }();
if (metadata.first != nullptr) {
title_version = metadata.first->GetVersionString();
title_name = metadata.first->GetApplicationName();
@@ -1128,7 +1161,7 @@ void GMainWindow::BootGame(const QString& filename) {
LOG_INFO(Frontend, "Booting game: {:016X} | {} | {}", title_id, title_name, title_version);
UpdateWindowTitle(title_name, title_version);
- loading_screen->Prepare(Core::System::GetInstance().GetAppLoader());
+ loading_screen->Prepare(system.GetAppLoader());
loading_screen->show();
emulation_running = true;
@@ -1176,11 +1209,12 @@ void GMainWindow::ShutdownGame() {
render_window->hide();
loading_screen->hide();
loading_screen->Clear();
- if (game_list->isEmpty())
+ if (game_list->IsEmpty()) {
game_list_placeholder->show();
- else
+ } else {
game_list->show();
- game_list->setFilterFocus();
+ }
+ game_list->SetFilterFocus();
setMouseTracking(false);
ui.centralwidget->setMouseTracking(false);
@@ -1246,16 +1280,18 @@ void GMainWindow::OnGameListOpenFolder(u64 program_id, GameListOpenTarget target
const std::string& game_path) {
std::string path;
QString open_target;
+ auto& system = Core::System::GetInstance();
- const auto [user_save_size, device_save_size] = [this, &program_id, &game_path] {
- FileSys::PatchManager pm{program_id};
+ const auto [user_save_size, device_save_size] = [this, &game_path, &program_id, &system] {
+ const FileSys::PatchManager pm{program_id, system.GetFileSystemController(),
+ system.GetContentProvider()};
const auto control = pm.GetControlMetadata().first;
if (control != nullptr) {
return std::make_pair(control->GetDefaultNormalSaveSize(),
control->GetDeviceSaveDataSize());
} else {
const auto file = Core::GetGameFileFromPath(vfs, game_path);
- const auto loader = Loader::GetLoader(file);
+ const auto loader = Loader::GetLoader(system, file);
FileSys::NACP nacp{};
loader->ReadControlData(nacp);
@@ -1557,7 +1593,8 @@ void GMainWindow::RemoveCustomConfiguration(u64 program_id) {
const QString config_dir =
QString::fromStdString(Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir));
const QString custom_config_file_path =
- config_dir + QString::fromStdString(fmt::format("{:016X}.ini", program_id));
+ config_dir + QStringLiteral("custom") + QDir::separator() +
+ QString::fromStdString(fmt::format("{:016X}.ini", program_id));
if (!QFile::exists(custom_config_file_path)) {
QMessageBox::warning(this, tr("Error Removing Custom Configuration"),
@@ -1581,7 +1618,8 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
"cancelled the operation."));
};
- const auto loader = Loader::GetLoader(vfs->OpenFile(game_path, FileSys::Mode::Read));
+ auto& system = Core::System::GetInstance();
+ const auto loader = Loader::GetLoader(system, vfs->OpenFile(game_path, FileSys::Mode::Read));
if (loader == nullptr) {
failed();
return;
@@ -1593,7 +1631,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
return;
}
- const auto& installed = Core::System::GetInstance().GetContentProvider();
+ const auto& installed = system.GetContentProvider();
const auto romfs_title_id = SelectRomFSDumpTarget(installed, program_id);
if (!romfs_title_id) {
@@ -1608,7 +1646,7 @@ void GMainWindow::OnGameListDumpRomFS(u64 program_id, const std::string& game_pa
if (*romfs_title_id == program_id) {
const u64 ivfc_offset = loader->ReadRomFSIVFCOffset();
- FileSys::PatchManager pm{program_id};
+ const FileSys::PatchManager pm{program_id, system.GetFileSystemController(), installed};
romfs = pm.PatchRomFS(file, ivfc_offset, FileSys::ContentRecordType::Program);
} else {
romfs = installed.GetEntry(*romfs_title_id, FileSys::ContentRecordType::Data)->GetRomFS();
@@ -1725,7 +1763,8 @@ void GMainWindow::OnGameListShowList(bool show) {
void GMainWindow::OnGameListOpenPerGameProperties(const std::string& file) {
u64 title_id{};
const auto v_file = Core::GetGameFileFromPath(vfs, file);
- const auto loader = Loader::GetLoader(v_file);
+ const auto loader = Loader::GetLoader(Core::System::GetInstance(), v_file);
+
if (loader == nullptr || loader->ReadProgramId(title_id) != Loader::ResultStatus::Success) {
QMessageBox::information(this, tr("Properties"),
tr("The game properties could not be loaded."));
@@ -2046,6 +2085,7 @@ void GMainWindow::OnStartGame() {
emu_thread->SetRunning(true);
+ qRegisterMetaType<Core::Frontend::ControllerParameters>("Core::Frontend::ControllerParameters");
qRegisterMetaType<Core::Frontend::SoftwareKeyboardParameters>(
"Core::Frontend::SoftwareKeyboardParameters");
qRegisterMetaType<Core::System::ResultStatus>("Core::System::ResultStatus");
@@ -2213,7 +2253,7 @@ void GMainWindow::OnConfigure() {
const auto old_theme = UISettings::values.theme;
const bool old_discord_presence = UISettings::values.enable_discord_presence;
- ConfigureDialog configure_dialog(this, hotkey_registry);
+ ConfigureDialog configure_dialog(this, hotkey_registry, input_subsystem.get());
connect(&configure_dialog, &ConfigureDialog::LanguageChanged, this,
&GMainWindow::OnLanguageChanged);
@@ -2337,11 +2377,11 @@ void GMainWindow::OnAbout() {
}
void GMainWindow::OnToggleFilterBar() {
- game_list->setFilterVisible(ui.action_Show_Filter_Bar->isChecked());
+ game_list->SetFilterVisible(ui.action_Show_Filter_Bar->isChecked());
if (ui.action_Show_Filter_Bar->isChecked()) {
- game_list->setFilterFocus();
+ game_list->SetFilterFocus();
} else {
- game_list->clearFilter();
+ game_list->ClearFilter();
}
}
@@ -2372,6 +2412,29 @@ void GMainWindow::OnCaptureScreenshot() {
OnStartGame();
}
+// TODO: Written 2020-10-01: Remove per-game config migration code when it is irrelevant
+void GMainWindow::MigrateConfigFiles() {
+ const std::string& config_dir_str = Common::FS::GetUserPath(Common::FS::UserPath::ConfigDir);
+ const QDir config_dir = QDir(QString::fromStdString(config_dir_str));
+ const QStringList config_dir_list = config_dir.entryList(QStringList(QStringLiteral("*.ini")));
+
+ Common::FS::CreateFullPath(fmt::format("{}custom" DIR_SEP, config_dir_str));
+ for (QStringList::const_iterator it = config_dir_list.constBegin();
+ it != config_dir_list.constEnd(); ++it) {
+ const auto filename = it->toStdString();
+ if (filename.find_first_not_of("0123456789abcdefACBDEF", 0) < 16) {
+ continue;
+ }
+ const auto origin = fmt::format("{}{}", config_dir_str, filename);
+ const auto destination = fmt::format("{}custom" DIR_SEP "{}", config_dir_str, filename);
+ LOG_INFO(Frontend, "Migrating config file from {} to {}", origin, destination);
+ if (!Common::FS::Rename(origin, destination)) {
+ // Delete the old config file if one already exists in the new location.
+ Common::FS::Delete(origin);
+ }
+ }
+}
+
void GMainWindow::UpdateWindowTitle(const std::string& title_name,
const std::string& title_version) {
const auto full_name = std::string(Common::g_build_fullname);
@@ -2429,7 +2492,7 @@ void GMainWindow::UpdateStatusBar() {
}
void GMainWindow::UpdateStatusButtons() {
- dock_status_button->setChecked(Settings::values.use_docked_mode);
+ dock_status_button->setChecked(Settings::values.use_docked_mode.GetValue());
multicore_status_button->setChecked(Settings::values.use_multi_core.GetValue());
Settings::values.use_asynchronous_gpu_emulation.SetValue(
Settings::values.use_asynchronous_gpu_emulation.GetValue() ||
@@ -2568,8 +2631,10 @@ void GMainWindow::OnReinitializeKeys(ReinitializeKeyBehavior behavior) {
const auto function = [this, &keys, &pdm] {
keys.PopulateFromPartitionData(pdm);
- Core::System::GetInstance().GetFileSystemController().CreateFactories(*vfs);
- keys.DeriveETicket(pdm);
+
+ auto& system = Core::System::GetInstance();
+ system.GetFileSystemController().CreateFactories(*vfs);
+ keys.DeriveETicket(pdm, system.GetContentProvider());
};
QString errors;
diff --git a/src/yuzu/main.h b/src/yuzu/main.h
index 01f9131e5..b380a66f3 100644
--- a/src/yuzu/main.h
+++ b/src/yuzu/main.h
@@ -37,15 +37,24 @@ enum class InstalledEntryType;
class GameListPlaceholder;
namespace Core::Frontend {
+struct ControllerParameters;
struct SoftwareKeyboardParameters;
} // namespace Core::Frontend
+namespace DiscordRPC {
+class DiscordInterface;
+}
+
namespace FileSys {
class ContentProvider;
class ManualContentProvider;
class VfsFilesystem;
} // namespace FileSys
+namespace InputCommon {
+class InputSubsystem;
+}
+
enum class EmulatedDirectoryTarget {
NAND,
SDMC,
@@ -62,10 +71,6 @@ enum class ReinitializeKeyBehavior {
Warning,
};
-namespace DiscordRPC {
-class DiscordInterface;
-}
-
class GMainWindow : public QMainWindow {
Q_OBJECT
@@ -86,8 +91,6 @@ public:
GMainWindow();
~GMainWindow() override;
- std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
-
bool DropAction(QDropEvent* event);
void AcceptDropEvent(QDropEvent* event);
@@ -114,9 +117,12 @@ signals:
void UpdateInstallProgress();
+ void ControllerSelectorReconfigureFinished();
+
void ErrorDisplayFinished();
void ProfileSelectorFinishedSelection(std::optional<Common::UUID> uuid);
+
void SoftwareKeyboardFinishedText(std::optional<std::u16string> text);
void SoftwareKeyboardFinishedCheckDialog();
@@ -125,6 +131,8 @@ signals:
public slots:
void OnLoadComplete();
+ void ControllerSelectorReconfigureControllers(
+ const Core::Frontend::ControllerParameters& parameters);
void ErrorDisplayDisplayError(QString body);
void ProfileSelectorSelectProfile();
void SoftwareKeyboardGetText(const Core::Frontend::SoftwareKeyboardParameters& parameters);
@@ -243,6 +251,7 @@ private:
std::optional<u64> SelectRomFSDumpTarget(const FileSys::ContentProvider&, u64 program_id);
InstallResult InstallNSPXCI(const QString& filename);
InstallResult InstallNCA(const QString& filename);
+ void MigrateConfigFiles();
void UpdateWindowTitle(const std::string& title_name = {},
const std::string& title_version = {});
void UpdateStatusBar();
@@ -255,6 +264,9 @@ private:
Ui::MainWindow ui;
+ std::unique_ptr<DiscordRPC::DiscordInterface> discord_rpc;
+ std::shared_ptr<InputCommon::InputSubsystem> input_subsystem;
+
GRenderWindow* render_window;
GameList* game_list;
LoadingScreen* loading_screen;
diff --git a/src/yuzu/main.ui b/src/yuzu/main.ui
index 87ea985d8..2f3792247 100644
--- a/src/yuzu/main.ui
+++ b/src/yuzu/main.ui
@@ -293,7 +293,7 @@
<bool>false</bool>
</property>
<property name="text">
- <string>Configure Current Game..</string>
+ <string>Configure Current Game...</string>
</property>
</action>
</widget>
diff --git a/src/yuzu/uisettings.cpp b/src/yuzu/uisettings.cpp
index a51175f36..37499fc85 100644
--- a/src/yuzu/uisettings.cpp
+++ b/src/yuzu/uisettings.cpp
@@ -16,4 +16,5 @@ const Themes themes{{
}};
Values values = {};
+
} // namespace UISettings
diff --git a/src/yuzu/uisettings.h b/src/yuzu/uisettings.h
index 2d2e82f15..ce3945485 100644
--- a/src/yuzu/uisettings.h
+++ b/src/yuzu/uisettings.h
@@ -87,9 +87,6 @@ struct Values {
// logging
bool show_console;
- // Controllers
- int profile_index;
-
// Game List
bool show_add_ons;
uint32_t icon_size;
@@ -100,6 +97,7 @@ struct Values {
};
extern Values values;
+
} // namespace UISettings
Q_DECLARE_METATYPE(UISettings::GameDir*);
diff --git a/src/yuzu_cmd/CMakeLists.txt b/src/yuzu_cmd/CMakeLists.txt
index a15719a0f..57f9916f6 100644
--- a/src/yuzu_cmd/CMakeLists.txt
+++ b/src/yuzu_cmd/CMakeLists.txt
@@ -39,7 +39,5 @@ endif()
if (MSVC)
include(CopyYuzuSDLDeps)
- include(CopyYuzuUnicornDeps)
copy_yuzu_SDL_deps(yuzu-cmd)
- copy_yuzu_unicorn_deps(yuzu-cmd)
endif()
diff --git a/src/yuzu_cmd/config.cpp b/src/yuzu_cmd/config.cpp
index 8a63fd191..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{
void Config::ReadValues() {
// Controls
- for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
const auto group = fmt::format("ControlsP{}", p);
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
std::string default_param = InputCommon::GenerateKeyboardParam(default_buttons[i]);
- Settings::values.players[p].buttons[i] =
+ Settings::values.players.GetValue()[p].buttons[i] =
sdl2_config->Get(group, Settings::NativeButton::mapping[i], default_param);
- if (Settings::values.players[p].buttons[i].empty())
- Settings::values.players[p].buttons[i] = default_param;
+ if (Settings::values.players.GetValue()[p].buttons[i].empty())
+ Settings::values.players.GetValue()[p].buttons[i] = default_param;
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
std::string default_param = InputCommon::GenerateAnalogParamFromKeys(
default_analogs[i][0], default_analogs[i][1], default_analogs[i][2],
default_analogs[i][3], default_analogs[i][4], 0.5f);
- Settings::values.players[p].analogs[i] =
+ Settings::values.players.GetValue()[p].analogs[i] =
sdl2_config->Get(group, Settings::NativeAnalog::mapping[i], default_param);
- if (Settings::values.players[p].analogs[i].empty())
- Settings::values.players[p].analogs[i] = default_param;
+ if (Settings::values.players.GetValue()[p].analogs[i].empty())
+ Settings::values.players.GetValue()[p].analogs[i] = default_param;
}
}
@@ -288,6 +288,12 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = default_param;
}
+ Settings::values.vibration_enabled.SetValue(
+ sdl2_config->GetBoolean("ControlsGeneral", "vibration_enabled", true));
+ Settings::values.enable_accurate_vibrations.SetValue(
+ sdl2_config->GetBoolean("ControlsGeneral", "enable_accurate_vibrations", false));
+ Settings::values.motion_enabled.SetValue(
+ sdl2_config->GetBoolean("ControlsGeneral", "motion_enabled", true));
Settings::values.touchscreen.enabled =
sdl2_config->GetBoolean("ControlsGeneral", "touch_enabled", true);
Settings::values.touchscreen.device =
@@ -339,7 +345,8 @@ void Config::ReadValues() {
Settings::values.gamecard_path = sdl2_config->Get("Data Storage", "gamecard_path", "");
// System
- Settings::values.use_docked_mode = sdl2_config->GetBoolean("System", "use_docked_mode", false);
+ Settings::values.use_docked_mode.SetValue(
+ sdl2_config->GetBoolean("System", "use_docked_mode", false));
const auto size = sdl2_config->GetInteger("System", "users_size", 0);
Settings::values.current_user = std::clamp<int>(
@@ -367,7 +374,7 @@ void Config::ReadValues() {
// Core
Settings::values.use_multi_core.SetValue(
- sdl2_config->GetBoolean("Core", "use_multi_core", false));
+ sdl2_config->GetBoolean("Core", "use_multi_core", true));
// Renderer
const int renderer_backend = sdl2_config->GetInteger(
@@ -391,11 +398,11 @@ void Config::ReadValues() {
const int gpu_accuracy_level = sdl2_config->GetInteger("Renderer", "gpu_accuracy", 0);
Settings::values.gpu_accuracy.SetValue(static_cast<Settings::GPUAccuracy>(gpu_accuracy_level));
Settings::values.use_asynchronous_gpu_emulation.SetValue(
- sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", false));
+ sdl2_config->GetBoolean("Renderer", "use_asynchronous_gpu_emulation", true));
Settings::values.use_vsync.SetValue(
static_cast<u16>(sdl2_config->GetInteger("Renderer", "use_vsync", 1)));
Settings::values.use_assembly_shaders.SetValue(
- sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", false));
+ sdl2_config->GetBoolean("Renderer", "use_assembly_shaders", true));
Settings::values.use_asynchronous_shaders.SetValue(
sdl2_config->GetBoolean("Renderer", "use_asynchronous_shaders", false));
Settings::values.use_asynchronous_shaders.SetValue(
diff --git a/src/yuzu_cmd/default_ini.h b/src/yuzu_cmd/default_ini.h
index aa9e40380..bcbbcd4ca 100644
--- a/src/yuzu_cmd/default_ini.h
+++ b/src/yuzu_cmd/default_ini.h
@@ -65,6 +65,14 @@ button_screenshot=
lstick=
rstick=
+# Whether to enable or disable vibration
+# 0: Disabled, 1 (default): Enabled
+vibration_enabled=
+
+# Whether to enable or disable accurate vibrations
+# 0 (default): Disabled, 1: Enabled
+enable_accurate_vibrations=
+
# for motion input, the following devices are available:
# - "motion_emu" (default) for emulating motion input from mouse input. Required parameters:
# - "update_period": update period in milliseconds (default to 100)
@@ -94,7 +102,7 @@ udp_pad_index=
[Core]
# Whether to use multi-core for CPU emulation
-# 0 (default): Disabled, 1: Enabled
+# 0: Disabled, 1 (default): Enabled
use_multi_core=
[Cpu]
@@ -163,7 +171,7 @@ max_anisotropy =
use_vsync =
# Whether to use OpenGL assembly shaders or not. NV_gpu_program5 is required.
-# 0 (default): Off, 1: On
+# 0: Off, 1 (default): On
use_assembly_shaders =
# Whether to allow asynchronous shader building.
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
index e5e684206..521209622 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.cpp
@@ -13,23 +13,24 @@
#include "input_common/sdl/sdl.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
-EmuWindow_SDL2::EmuWindow_SDL2(Core::System& system, bool fullscreen) : system{system} {
+EmuWindow_SDL2::EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem_)
+ : input_subsystem{input_subsystem_} {
if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0) {
LOG_CRITICAL(Frontend, "Failed to initialize SDL2! Exiting...");
exit(1);
}
- InputCommon::Init();
+ input_subsystem->Initialize();
SDL_SetMainReady();
}
EmuWindow_SDL2::~EmuWindow_SDL2() {
- InputCommon::Shutdown();
+ input_subsystem->Shutdown();
SDL_Quit();
}
void EmuWindow_SDL2::OnMouseMotion(s32 x, s32 y) {
TouchMoved((unsigned)std::max(x, 0), (unsigned)std::max(y, 0));
- InputCommon::GetMotionEmu()->Tilt(x, y);
+ input_subsystem->GetMotionEmu()->Tilt(x, y);
}
void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
@@ -41,9 +42,9 @@ void EmuWindow_SDL2::OnMouseButton(u32 button, u8 state, s32 x, s32 y) {
}
} else if (button == SDL_BUTTON_RIGHT) {
if (state == SDL_PRESSED) {
- InputCommon::GetMotionEmu()->BeginTilt(x, y);
+ input_subsystem->GetMotionEmu()->BeginTilt(x, y);
} else {
- InputCommon::GetMotionEmu()->EndTilt();
+ input_subsystem->GetMotionEmu()->EndTilt();
}
}
}
@@ -79,9 +80,9 @@ void EmuWindow_SDL2::OnFingerUp() {
void EmuWindow_SDL2::OnKeyEvent(int key, u8 state) {
if (state == SDL_PRESSED) {
- InputCommon::GetKeyboard()->PressKey(key);
+ input_subsystem->GetKeyboard()->PressKey(key);
} else if (state == SDL_RELEASED) {
- InputCommon::GetKeyboard()->ReleaseKey(key);
+ input_subsystem->GetKeyboard()->ReleaseKey(key);
}
}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2.h b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
index fffac4252..53d756c3c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2.h
@@ -14,9 +14,13 @@ namespace Core {
class System;
}
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2 : public Core::Frontend::EmuWindow {
public:
- explicit EmuWindow_SDL2(Core::System& system, bool fullscreen);
+ explicit EmuWindow_SDL2(InputCommon::InputSubsystem* input_subsystem);
~EmuWindow_SDL2();
/// Polls window events
@@ -28,9 +32,6 @@ public:
/// Returns if window is shown (not minimized)
bool IsShown() const override;
- /// Presents the next frame
- virtual void Present() = 0;
-
protected:
/// Called by PollEvents when a key is pressed or released.
void OnKeyEvent(int key, u8 state);
@@ -62,9 +63,6 @@ protected:
/// Called when a configuration change affects the minimal size of the window
void OnMinimalClientAreaChangeRequest(std::pair<unsigned, unsigned> minimal_size) override;
- /// Instance of the system, used to access renderer for the presentation thread
- Core::System& system;
-
/// Is the window still open?
bool is_open = true;
@@ -76,4 +74,7 @@ protected:
/// Keeps track of how often to update the title bar during gameplay
u32 last_time = 0;
+
+ /// Input subsystem to use with this window.
+ InputCommon::InputSubsystem* input_subsystem;
};
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
index e78025737..5f35233b5 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.cpp
@@ -87,8 +87,8 @@ bool EmuWindow_SDL2_GL::SupportsRequiredGLExtensions() {
return unsupported_ext.empty();
}
-EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(Core::System& system, bool fullscreen)
- : EmuWindow_SDL2{system, fullscreen} {
+EmuWindow_SDL2_GL::EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen)
+ : EmuWindow_SDL2{input_subsystem} {
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 4);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 3);
SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
@@ -162,13 +162,3 @@ EmuWindow_SDL2_GL::~EmuWindow_SDL2_GL() {
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_GL::CreateSharedContext() const {
return std::make_unique<SDLGLContext>();
}
-
-void EmuWindow_SDL2_GL::Present() {
- SDL_GL_MakeCurrent(render_window, window_context);
- SDL_GL_SetSwapInterval(Settings::values.use_vsync.GetValue() ? 1 : 0);
- while (IsOpen()) {
- system.Renderer().TryPresent(100);
- SDL_GL_SwapWindow(render_window);
- }
- SDL_GL_MakeCurrent(render_window, nullptr);
-}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
index 48bb41683..dba5c293c 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_gl.h
@@ -8,13 +8,15 @@
#include "core/frontend/emu_window.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2_GL final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_GL(Core::System& system, bool fullscreen);
+ explicit EmuWindow_SDL2_GL(InputCommon::InputSubsystem* input_subsystem, bool fullscreen);
~EmuWindow_SDL2_GL();
- void Present() override;
-
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
private:
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
index cb8e68a39..3ba657c00 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.cpp
@@ -19,8 +19,8 @@
#include <SDL.h>
#include <SDL_syswm.h>
-EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(Core::System& system, bool fullscreen)
- : EmuWindow_SDL2{system, fullscreen} {
+EmuWindow_SDL2_VK::EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem)
+ : EmuWindow_SDL2{input_subsystem} {
const std::string window_title = fmt::format("yuzu {} | {}-{} (Vulkan)", Common::g_build_name,
Common::g_scm_branch, Common::g_scm_desc);
render_window =
@@ -73,7 +73,3 @@ EmuWindow_SDL2_VK::~EmuWindow_SDL2_VK() = default;
std::unique_ptr<Core::Frontend::GraphicsContext> EmuWindow_SDL2_VK::CreateSharedContext() const {
return std::make_unique<DummyContext>();
}
-
-void EmuWindow_SDL2_VK::Present() {
- // TODO (bunnei): ImplementMe
-}
diff --git a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
index 77a6ca72b..bdfdc3c6f 100644
--- a/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
+++ b/src/yuzu_cmd/emu_window/emu_window_sdl2_vk.h
@@ -13,12 +13,14 @@ namespace Core {
class System;
}
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2_VK final : public EmuWindow_SDL2 {
public:
- explicit EmuWindow_SDL2_VK(Core::System& system, bool fullscreen);
- ~EmuWindow_SDL2_VK();
-
- void Present() override;
+ explicit EmuWindow_SDL2_VK(InputCommon::InputSubsystem* input_subsystem);
+ ~EmuWindow_SDL2_VK() override;
std::unique_ptr<Core::Frontend::GraphicsContext> CreateSharedContext() const override;
};
diff --git a/src/yuzu_cmd/yuzu.cpp b/src/yuzu_cmd/yuzu.cpp
index 8efe49390..3a76c785f 100644
--- a/src/yuzu_cmd/yuzu.cpp
+++ b/src/yuzu_cmd/yuzu.cpp
@@ -23,12 +23,15 @@
#include "common/telemetry.h"
#include "core/core.h"
#include "core/crypto/key_manager.h"
+#include "core/file_sys/registered_cache.h"
#include "core/file_sys/vfs_real.h"
#include "core/gdbstub/gdbstub.h"
+#include "core/hle/kernel/process.h"
#include "core/hle/service/filesystem/filesystem.h"
#include "core/loader/loader.h"
#include "core/settings.h"
#include "core/telemetry_session.h"
+#include "input_common/main.h"
#include "video_core/renderer_base.h"
#include "yuzu_cmd/config.h"
#include "yuzu_cmd/emu_window/emu_window_sdl2.h"
@@ -37,8 +40,6 @@
#include "yuzu_cmd/emu_window/emu_window_sdl2_vk.h"
#endif
-#include "core/file_sys/registered_cache.h"
-
#ifdef _WIN32
// windows.h needs to be included before shellapi.h
#include <windows.h>
@@ -179,15 +180,16 @@ int main(int argc, char** argv) {
Settings::Apply();
Core::System& system{Core::System::GetInstance()};
+ InputCommon::InputSubsystem input_subsystem;
std::unique_ptr<EmuWindow_SDL2> emu_window;
switch (Settings::values.renderer_backend.GetValue()) {
case Settings::RendererBackend::OpenGL:
- emu_window = std::make_unique<EmuWindow_SDL2_GL>(system, fullscreen);
+ emu_window = std::make_unique<EmuWindow_SDL2_GL>(&input_subsystem, fullscreen);
break;
case Settings::RendererBackend::Vulkan:
#ifdef HAS_VULKAN
- emu_window = std::make_unique<EmuWindow_SDL2_VK>(system, fullscreen);
+ emu_window = std::make_unique<EmuWindow_SDL2_VK>(&input_subsystem);
break;
#else
LOG_CRITICAL(Frontend, "Vulkan backend has not been compiled!");
@@ -234,16 +236,15 @@ int main(int argc, char** argv) {
// Core is loaded, start the GPU (makes the GPU contexts current to this thread)
system.GPU().Start();
- system.Renderer().Rasterizer().LoadDiskResources();
+ system.Renderer().Rasterizer().LoadDiskResources(
+ system.CurrentProcess()->GetTitleID(), false,
+ [](VideoCore::LoadCallbackStage, size_t value, size_t total) {});
- std::thread render_thread([&emu_window] { emu_window->Present(); });
system.Run();
while (emu_window->IsOpen()) {
std::this_thread::sleep_for(std::chrono::milliseconds(1));
}
system.Pause();
- render_thread.join();
-
system.Shutdown();
detached_tasks.WaitForAllTasks();
diff --git a/src/yuzu_tester/CMakeLists.txt b/src/yuzu_tester/CMakeLists.txt
index 06c2ee011..d8a2a1511 100644
--- a/src/yuzu_tester/CMakeLists.txt
+++ b/src/yuzu_tester/CMakeLists.txt
@@ -28,7 +28,5 @@ endif()
if (MSVC)
include(CopyYuzuSDLDeps)
- include(CopyYuzuUnicornDeps)
copy_yuzu_SDL_deps(yuzu-tester)
- copy_yuzu_unicorn_deps(yuzu-tester)
endif()
diff --git a/src/yuzu_tester/config.cpp b/src/yuzu_tester/config.cpp
index 74022af23..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) {
void Config::ReadValues() {
// Controls
- for (std::size_t p = 0; p < Settings::values.players.size(); ++p) {
+ for (std::size_t p = 0; p < Settings::values.players.GetValue().size(); ++p) {
for (int i = 0; i < Settings::NativeButton::NumButtons; ++i) {
- Settings::values.players[p].buttons[i] = "";
+ Settings::values.players.GetValue()[p].buttons[i] = "";
}
for (int i = 0; i < Settings::NativeAnalog::NumAnalogs; ++i) {
- Settings::values.players[p].analogs[i] = "";
+ Settings::values.players.GetValue()[p].analogs[i] = "";
}
}
@@ -75,6 +75,9 @@ void Config::ReadValues() {
Settings::values.debug_pad_analogs[i] = "";
}
+ Settings::values.vibration_enabled.SetValue(true);
+ Settings::values.enable_accurate_vibrations.SetValue(false);
+ Settings::values.motion_enabled.SetValue(true);
Settings::values.touchscreen.enabled = "";
Settings::values.touchscreen.device = "";
Settings::values.touchscreen.finger = 0;
@@ -82,8 +85,8 @@ void Config::ReadValues() {
Settings::values.touchscreen.diameter_x = 15;
Settings::values.touchscreen.diameter_y = 15;
- Settings::values.use_docked_mode =
- sdl2_config->GetBoolean("Controls", "use_docked_mode", false);
+ Settings::values.use_docked_mode.SetValue(
+ sdl2_config->GetBoolean("Controls", "use_docked_mode", false));
// Data Storage
Settings::values.use_virtual_sd =
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
index 8584f6671..78f75fb38 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.cpp
@@ -13,7 +13,6 @@
#include <glad/glad.h>
-#include "common/assert.h"
#include "common/logging/log.h"
#include "common/scm_rev.h"
#include "core/settings.h"
@@ -53,7 +52,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
exit(1);
}
- InputCommon::Init();
+ input_subsystem->Initialize();
SDL_SetMainReady();
@@ -105,7 +104,7 @@ EmuWindow_SDL2_Hide::EmuWindow_SDL2_Hide() {
}
EmuWindow_SDL2_Hide::~EmuWindow_SDL2_Hide() {
- InputCommon::Shutdown();
+ input_subsystem->Shutdown();
SDL_GL_DeleteContext(gl_context);
SDL_Quit();
}
diff --git a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
index c13a82df2..a553b4b95 100644
--- a/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
+++ b/src/yuzu_tester/emu_window/emu_window_sdl2_hide.h
@@ -8,6 +8,10 @@
struct SDL_Window;
+namespace InputCommon {
+class InputSubsystem;
+}
+
class EmuWindow_SDL2_Hide : public Core::Frontend::EmuWindow {
public:
explicit EmuWindow_SDL2_Hide();
@@ -25,6 +29,8 @@ private:
/// Whether the GPU and driver supports the OpenGL extension required
bool SupportsRequiredGLExtensions();
+ std::unique_ptr<InputCommon::InputSubsystem> input_subsystem;
+
/// Internal SDL2 render window
SDL_Window* render_window;
diff --git a/src/yuzu_tester/yuzu.cpp b/src/yuzu_tester/yuzu.cpp
index 7acf0caad..5798ce43a 100644
--- a/src/yuzu_tester/yuzu.cpp
+++ b/src/yuzu_tester/yuzu.cpp
@@ -255,7 +255,6 @@ int main(int argc, char** argv) {
"SDLHideTester");
system.GPU().Start();
- system.Renderer().Rasterizer().LoadDiskResources();
system.Run();
while (!finished) {